如何将匿名类型作为参数传递?

如何将匿名类型作为参数传递给其他函数:

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);

这里的变量 query没有强类型。我应该如何定义我的 LogEmployees函数来接受它?

public void LogEmployees (? list)
{
foreach (? item in list)
{


}
}

换句话说,我应该使用什么来代替 ?标记。

171035 次浏览

不幸的是,你想做的事是不可能的。在引擎盖下,查询变量的类型为匿名类型的 IEnumerable。匿名类型名称不能在用户代码中表示,因此无法将它们作为函数的输入参数。

最好的办法是创建一个类型,并将其用作查询的返回值,然后将其传递给函数。比如说,

struct Data {
public string ColumnName;
}


var query = (from name in some.Table
select new Data { ColumnName = name });
MethodOp(query);
...
MethodOp(IEnumerable<Data> enumerable);

但是在这种情况下,您只选择一个字段,因此直接选择该字段可能更容易。这将导致查询类型为字段类型的 IEnumerable。在这种情况下,列名称。

var query = (from name in some.Table select name);  // IEnumerable<string>

通常,您可以使用泛型进行此操作,例如:

MapEntToObj<T>(IQueryable<T> query) {...}

然后,当您调用 MapEntToObj(query)时,编译器应该推断出 T。不太确定你想在这个方法中做什么,所以我不知道这是否有用... 问题是在 MapEntToObj中你仍然不能命名 T-你可以:

  • T调用其他泛型方法
  • 使用 T的反射来做事情

但是除此之外,很难操作匿名类型-不仅仅是因为它们是不可变的;-p

另一个技巧(当 正在提取数据时)是也传递一个选择器-例如:

Foo<TSource, TValue>(IEnumerable<TSource> source,
Func<TSource,string> name) {
foreach(TSource item in source) Console.WriteLine(name(item));
}
...
Foo(query, x=>x.Title);

不能将匿名类型传递给非泛型函数,除非参数类型为 object

public void LogEmployees (object obj)
{
var list = obj as IEnumerable();
if (list == null)
return;


foreach (var item in list)
{


}
}

匿名类型用于方法中的短期使用。

来自 MSDN-匿名类型:

不能将字段、属性、事件或方法的返回类型声明为具有匿名类型。类似地,不能将方法、属性、构造函数或索引器的形式参数声明为具有匿名类型。若要将匿名类型或包含匿名类型的集合作为方法的参数传递,可以将参数声明为类型对象.但是,这样做会破坏强类型的目的。

(强调我的)


更新

您可以使用泛型来实现您想要的结果:

public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{


}
}

你可以这样做:

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list
{
foreach (T item in list)
{


}
}

但是你不会对每件东西都做很多事情。您可以调用 ToString,但是不能直接使用(比如) NameId

如果您知道,您的结果实现了一个特定的接口,您可以使用该接口作为数据类型:

public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{


}
}

我将使用 IEnumerable<object>作为参数的类型。但是对于不可避免的显式强制转换来说,这并不是一个很大的收获。 干杯

我认为你应该为这种匿名类型创建一个类。在我看来,这是最明智的做法。但如果你真的不想,你可以使用动力学:

public void LogEmployees (IEnumerable<dynamic> list)
{
foreach (dynamic item in list)
{
string name = item.Name;
int id = item.Id;
}
}

注意,这是 没有强类型的,因此,例如,如果 Name 更改为 EmployeeName,那么在运行时之前您不会知道有问题。

可以通过以下技巧使用泛型(转换为匿名类型) :

public void LogEmployees<T>(IEnumerable<T> list)
{
foreach (T item in list)
{
var typedItem = Cast(item, new { Name = "", Id = 0 });
// now you can use typedItem.Name, etc.
}
}


static T Cast<T>(object obj, T type)
{
return (T)obj;
}

“动态”也可用于此目的。

var anonymousType = new { Id = 1, Name = "A" };


var anonymousTypes = new[] { new { Id = 1, Name = "A" }, new { Id = 2, Name = "B" };


private void DisplayAnonymousType(dynamic anonymousType)
{
}


private void DisplayAnonymousTypes(IEnumerable<dynamic> anonymousTypes)
{
foreach (var info in anonymousTypes)
{


}
}

不传递匿名类型,而是传递动态类型的 List:

  1. var dynamicResult = anonymousQueryResult.ToList<dynamic>();
  2. 方法签名: DoSomething(List<dynamic> _dynamicResult)
  3. 调用方法: DoSomething(dynamicResult);
  4. 搞定。

感谢 Petar Ivanov

若要传递匿名类型,请考虑使用 充满活力。下面显示了一个更长的示例以及您可以使用的技术。例如,考虑在这里调用 TreadSafeDynamicObject‘ CustomEmployee’以使代码更有意义。代码有一个构造函数,它接受一个动态对象(您的匿名的,可能是嵌套的类) ,例如: Var some CustomEmploye = new { 实习生 = 假, EmployeeFacts = new { 是真的, BirthDate = new DateTime (1960,1,1) } };

您可以使用下面所示的技术将 some CustomEmployee 转换为动态对象,例如将‘ some CustomEmployee’传递到构造函数中,在我的代码中,它将是:

dynamic someEmp = new ThreadSafeDynamicObject(someCustomEmployee);

一旦你把某个 Emp 转换成了一个合适的动态对象,你的 LogEmployee 函数就可以序列化这个对象并记录它,或者以其他方式处理它(注意,如果只是序列化这个匿名类实例就足够了的话,你无需通过将它转换成一个动态对象)。

例如:

dynamic threadSafeToyota = new ThreadSafeDynamicObject(new {
Make = "Toyota",
Model = "CR-H",
Propulsion = new {
IsHybrid = true,
UsesPetrol = true,
ElectricMotor = true
}
});

该代码接受一个动态对象,并使用一个私有方法‘ ToDictionary’来转换匿名类实例的对象图,这是设置动态对象属性的一种替代方法。

请注意,我还在这里添加了一些代码,以便在设置和获取属性时提供线程安全性。

public class ThreadSafeDynamicObject : DynamicObject, IEnumerable<KeyValuePair<string, object>>
{


public ThreadSafeDynamicObject()
{
}
    

public ThreadSafeDynamicObject(dynamic members)
{
var membersDict = ToDictionary(members);
InitMembers(membersDict);
}


private IDictionary<string, object> ToDictionary(object data)
{
var attr = BindingFlags.Public | BindingFlags.Instance;
var dict = new Dictionary<string, object>();
foreach (var property in data.GetType().GetProperties(attr))
{
if (property.CanRead)
{
dict.Add(property.Name, property.GetValue(data, null));
}
}
return dict;
}


private void InitMembers(IDictionary<string, object> membersDict)
{
foreach (KeyValuePair<string, object> member in membersDict){
_members.AddOrUpdate(member.Key, member.Value, (key, oldValue) => member.Value);
}
}


private readonly ConcurrentDictionary<string, object> _members = new();


public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _members.TryGetValue(binder.Name, out result);
}


public override bool TrySetMember(SetMemberBinder binder, object value)
{
_members.AddOrUpdate(binder.Name, value, (key, oldvalue) => value);
return true;
}


public override IEnumerable<string> GetDynamicMemberNames()
{
return _members.Keys.ToList().AsReadOnly();
}


public override string ToString()
{
return JsonSerializer.Serialize(_members);
}


public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return _members.GetEnumerator();
}


IEnumerator IEnumerable.GetEnumerator()
{
return _members.GetEnumerator();
}
}

当运行 Linqpad 7中的代码时,我得到了这个输出(我使用的是静态 System。控制台和使用系统。动态链接) :

WriteLine(threadSafe.ToString());
WriteLine(threadSafe.Make);
WriteLine(threadSafe.Model);
WriteLine(threadSafe.Propulsion.IsHybrid);
WriteLine(threadSafe.Propulsion.UsesPetrol);
WriteLine(threadSafe.Propulsion.ElectricMotor);

Output after outputting a anonymous object into a dictionary and using a dynamic object

这样做有几个好处。它支持在输出中可以看到的嵌套级别,并且非常灵活。“ ToDictionary”方法在这里是必不可少的。此外,我们不必在。网络框架,所以功能是内置的。我还没有检查这段代码的所有变体,至少它支持匿名类型对象图的典型场景。

这里的关键是首先将匿名类型转换为字典,然后使用派生的 DynamicObject 的成员(‘ fields’或‘ props’)填充内部并发字典。

有几种方法可以解决这个问题:

  • 你可以打拳击。例如,有一个接受对象并使用反射来提取属性并记录属性及其值的方法 例如:

    Public void LogEmployee (object some CustomEmployee){//. . }

  • 您可以将匿名对象转换为动态对象,如我的示例所示

  • 除了装箱或转换为动态对象之外,还可以通过序列化转换后的对象(装箱对象或动态变量)来避免反射。