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


143

如何将匿名类型作为参数传递给其他函数?考虑以下示例:

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

    }
}

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


1
更好的不同重复问题,用于处理传递参数而不是返回数据:stackoverflow.com/questions/16823658/…–
罗伯·丘奇

Answers:


183

我认为您应该为此匿名类型创建一个类。我认为这将是最明智的选择。但是,如果您真的不想这么做,可以使用动态:

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

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


由于dynamic使用原因,我将其检查为正确答案。我真的很方便。谢谢:)
Saeed Neamati 2011年

1
我同意,一旦开始传递数据,通常/应该首选结构化的方式,以免引入难以发现的错误(避免使用类型系统)。但是,如果您想找到一个折衷方案,则另一种方法是简单地传递通用词典。如今,C#字典初始化程序非常方便使用。
乔纳斯(Jonas)

在某些情况下,您需要通用实现,而传递硬类型意味着可能进行切换或工厂实现,这会使代码膨胀。如果您有一个真正的动态情况,并且不介意处理接收到的数据,那么这就是完美的选择。感谢您的答复@Tim S.
拉里·史密斯

42

您可以这样做:

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

    }
}

...但是您不会在每个项目上做太多事情。你可以调用toString,但你将无法使用(比方说)NameId直接。


2
除了可以where T : some type在第一行的末尾使用来缩小类型。但是,到那时,期望某种类型的通用接口将更有意义。:)
CassOnMars 2011年

9
@d_r_w:但是您不能使用where T : some type匿名类型,因为它们不实现任何类型的接口...
Jon Skeet

@dlev:您无法做到这一点,foreach要求在实现GetEnumerator上迭代该变量,并且匿名类型不能保证这一点。
CassOnMars 2011年

1
@乔恩·斯基特(Jon Skeet):好点,今天早上我的大脑功能不足。
CassOnMars 2011年

1
@JonSkeet。我想如果T是匿名类型,您可以使用反射来仍然访问/设置属性,对吗?我正在考虑有人编写“ Select * from”语句并使用匿名(或已定义)类来定义查询结果中的哪些列映射到匿名对象上相同命名属性的情况。
C. Tewalt 2015年

19

不幸的是,您试图做的事是不可能的。在后台,将查询变量键入为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>

我的例子是一个,但大多数情况下更多。您通过工作得到的答案(现在非常明显)。我只是需要休息一下才能想一想;-)
Tony Trembath-Drake


需要注意的是,当创建适当的类时,Equals行为会改变。即您必须实施它。(我知道这种差异,但在重构过程中仍然设法忘了它。)
LosManos

11

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

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

    foreach (var item in list)
    {

    }
}

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

从MSDN- 匿名类型

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

(强调我的)


更新资料

您可以使用泛型来实现所需的功能:

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

    }
}

4
如果您不能将匿名类型(或匿名类型的集合)传递给方法,则整个LINQ都会失败。您可以,只是该方法必须完全通用,而不使用匿名类型的属性。
乔恩·斯基特

2
重新objectdynamic; p
马克·格雷夫

如果使用“ as”进行转换,则应检查列表是否为空
Alex

“可以”!=“必须”。object根据我的回答,使用不同于使方法成为匿名类型的泛型。
乔恩·斯基特

8

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

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

然后,编译器应推断T何时调用MapEntToObj(query)。不太确定您想在该方法内执行什么操作,因此我无法确定这是否有用...问题是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);

1
学习了一些新知识,不知道匿名类型是不可变的!;)
Annie Lagang

1
@AnneLagang真正取决于编译器,因为它会生成它们。在VB.NET中,匿名类型可以是可变的。
Marc Gravell


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

6

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

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

   }
}

1
这是正确的答案!它只需要更多的爱:)
Korayem '16

2

除了传递匿名类型,还传递动态类型的列表:

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

感谢Petar Ivanov


0

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

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

    }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.