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

Posted

技术标签:

【中文标题】如何将匿名类型作为参数传递?【英文标题】:How to pass anonymous types as parameters? 【发布时间】:2011-10-01 06:35:44 【问题描述】:

如何将匿名类型作为参数传递给其他函数?考虑这个例子:

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)
    

    

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

【问题讨论】:

处理传递参数而不是返回数据的更好的不同重复问题:***.com/questions/16823658/… 【参考方案1】:

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

public void LogEmployees (IEnumerable<dynamic> list)

    foreach (dynamic item in list)
    
        string name = item.Name;
        int id = item.Id;
    

请注意,这不是强类型,因此,例如,如果 Name 更改为 EmployeeName,直到运行时您才会知道存在问题。

【讨论】:

由于dynamic 的使用,我将此作为正确答案进行了检查。我真的很方便。谢谢:) 我同意,一旦开始传递数据,通常可能/应该首选更结构化的方式,以免引入难以发现的错误(您正在回避类型系统)。但是,如果您想找到折衷方案,另一种方法是简单地传递通用字典。如今,C# 字典初始化器使用起来非常方便。 在某些情况下,您需要通用实现,而传递硬类型可能意味着切换或工厂实现开始使代码膨胀。如果您有一个真正动态的情况并且不介意稍微反思来处理您收到的数据,那么这是完美的。感谢@Tim S. 的回答。【参考方案2】:

你可以这样做:

public void LogEmployees<T>(List<T> list) // Or IEnumerable<T> list

    foreach (T item in list)
    

    

...但是您不会对每个项目做太多事情。你可以调用 ToString,但你不能直接使用(比如)NameId

【讨论】:

除了可以在第一行末尾使用where T : some type 来缩小类型。但是,在那个时候,期待某种类型的通用接口会更有意义地期待一个接口。 :) @d_r_w:您不能将where T : some type 与匿名类型一起使用,因为它们不实现任何类型的接口... @dlev:你不能这样做,foreach 要求在实现GetEnumerator 时迭代的变量,而匿名类型不能保证。 @Jon Skeet:很好,今天早上我的大脑动力不足。 @JonSkeet。我想如果 T 是匿名类型,你可以使用反射来访问/设置属性吗?我在考虑这样一种情况:有人编写“Select * from”语句并使用匿名(或已定义)类来定义查询结果中的哪些列映射到匿名对象上的相同命名属性。【参考方案3】:

不幸的是,您尝试做的事情是不可能的。在后台,查询变量被键入为匿名类型的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>

【讨论】:

我的例子是一个,但大多数时候它更多。您通过作品的答案(现在很明显)。不过,我只是需要休息一下吃午饭;-) 仅供参考:从***.com/questions/775387/…合并 需要注意的是,当您创建适当的类时,Equals 会改变行为。 IE。你必须实施它。 (我知道这种差异,但在重构过程中仍然设法忘记了它。)【参考方案4】:

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

public void LogEmployees (object obj)

    var list = obj as IEnumerable(); 
    if (list == null)
       return;

    foreach (var item in list)
    

    

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

来自 MSDN - Anonymous Types:

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

(强调我的)


更新

你可以使用泛型来实现你想要的:

public void LogEmployees<T>(IEnumerable<T> list)

    foreach (T item in list)
    

    

【讨论】:

如果不能将匿名类型(或匿名类型的集合)传递给方法,那么整个 LINQ 都会失败。可以,只是方法必须是完全通用的,不能使用匿名类型的属性。 回复 object - 或 dynamic ;p 如果使用 "as" 进行投射,您应该检查列表是否为空 “可以”!=“必须”。根据我的回答,使用 object 与在匿名类型中创建泛型方法不同。【参考方案5】:

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

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)
   

   

【讨论】:

这是正确答案!它只是需要更多的爱:)【参考方案6】:

通常,您使用泛型执行此操作,例如:

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);

【讨论】:

学到了新东西,不知道匿名类型是不可变的! ;) @AnneLagang 这真的取决于编译器,因为它会生成它们。在 VB.NET 中,匿名类型可以是可变的。 仅供参考:从***.com/questions/775387/…合并【参考方案7】:

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

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;

【讨论】:

【参考方案8】:

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

    var dynamicResult = anonymousQueryResult.ToList&lt;dynamic&gt;(); 方法签名:DoSomething(List&lt;dynamic&gt; _dynamicResult) 调用方法:DoSomething(dynamicResult); 完成。

感谢Petar Ivanov!

【讨论】:

【参考方案9】:

如果您知道,您的结果实现了某个接口,您可以将该接口用作数据类型:

public void LogEmployees<T>(IEnumerable<T> list)

    foreach (T item in list)
    

    

【讨论】:

【参考方案10】:

我会使用IEnumerable&lt;object&gt; 作为参数的类型。然而,对于不可避免的显式演员表来说并不是一个很大的收获。 干杯

【讨论】:

以上是关于如何将匿名类型作为参数传递?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 CoffeeScript 中将两个匿名函数作为参数传递?

Android:将匿名类作为 Intent 参数传递的异常

React 组件上调用方法如何传递参数,除了匿名函数用更好的方法吗

如何将注释类型作为方法参数传递?

如何将布尔数据类型作为存储过程的参数传递?

如何将类类型作为函数参数传递