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

Sae*_*ati 126 c# parameters function anonymous-types

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

var query = from employee in employees select new { Name = employee.Name, Id = employee.Id };
LogEmployees(query);
Run Code Online (Sandbox Code Playgroud)

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

换句话说,我应该使用什么而不是LogEmployees标记.

Tim*_* S. 163

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

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

请注意,这不是强类型的,因此,例如,如果名称更改为EmployeeName,则在运行时之前您不会知道存在问题.

  • 我同意,一旦开始传递数据,通常可能/应该首选更结构化的方式,以免引入难以发现的错误(您正在回避类型系统)。然而,如果你想找到一个折衷方案,另一种方法是简单地传递一个通用的 Dictionary。如今,C# 字典初始值设定项使用起来非常方便。 (2认同)

Jon*_*eet 41

你可以这样做:

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

    }
}
Run Code Online (Sandbox Code Playgroud)

......但是你不会对每件物品做很多事情.你可以调用toString,但你将无法使用(比方说)NameId直接.

  • @d_r_w:你不能使用`where T:some type`和匿名类型,因为它们没有实现任何类型的接口...... (9认同)
  • 除了您可以在第一行的末尾使用 `where T : some type` 来缩小类型。但是,在那时,期望某种类型的通用接口会更有意义。:) (2认同)

Jar*_*Par 18

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

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

struct Data {
  public string ColumnName; 
}

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

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

var query = (from name in some.Table select name);  // IEnumerable<string>
Run Code Online (Sandbox Code Playgroud)


Ode*_*ded 10

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

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

    foreach (var item in list)
    {

    }
}
Run Code Online (Sandbox Code Playgroud)

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

来自MSDN - 匿名类型:

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

(强调我的)


更新

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

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

    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您无法将匿名类型(或匿名类型的集合)传递给方法,则整个LINQ将失败.你可以,只是该方法必须完全通用,而不是使用匿名类型的属性. (2认同)
  • 重新`object` - 或`dynamic` ;p (2认同)

Mar*_*ell 7

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

MapEntToObj<T>(IQueryable<T> query) {...}
Run Code Online (Sandbox Code Playgroud)

然后编译器应该推断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);
Run Code Online (Sandbox Code Playgroud)


小智 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;
}
Run Code Online (Sandbox Code Playgroud)


Din*_*r P 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)
   {

   }
}
Run Code Online (Sandbox Code Playgroud)