【问题标题】:Is there a trick in creating a generic list of anonymous type?创建匿名类型的通用列表有技巧吗?
【发布时间】:2013-04-01 18:39:18
【问题描述】:

有时我需要使用元组,例如我有坦克列表和他们的目标坦克(他们追逐他们或类似的东西):

List<Tuple<Tank,Tank>> mylist = new List<Tuple<Tank,Tank>>();

然后当我遍历列表时,我通过

访问它们
mylist[i].item1 ...
mylist[i].item2 ...

这很令人困惑,我总是忘记什么是 item1 和什么是 item2,如果我可以通过以下方式访问它们:

mylist[i].AttackingTank...
mylist[i].TargetTank...

这样就清楚多了,有没有不定义类的方法:

MyTuple
{
public Tank AttackingTank;
public Tank TargetTank;
}

我想避免定义此类,因为那样我将不得不在不同的场景中定义许多不同的类,我可以做一些“技巧”并使其匿名。

类似:

var k = new {Name = "me", phone = 123};
mylist.Add(k);

当然,当我定义它时我没有类型可以传递给 List

【问题讨论】:

  • 列表类型的动态关键字你试过了吗?
  • 定义类有什么问题?似乎是显而易见的解决方案。至于需要define many different classes,或许有可以利用的继承结构?

标签: c# generics anonymous-types


【解决方案1】:

您可以为匿名类型创建一个空列表,然后使用它,享受完整的智能感知和编译时检查:

var list = Enumerable.Empty<object>()
             .Select(r => new {A = 0, B = 0}) // prototype of anonymous type
             .ToList();

list.Add(new { A = 4, B = 5 }); // adding actual values

Console.Write(list[0].A);

【讨论】:

  • 有一个方便的 Select 可以将对象转换为匿名类型,所以我们得到的是匿名类型列表,而不是对象列表。这个示例编译得很好。
  • 赞成,因为这与我的答案基本相同,而且这个答案早于我的答案。
  • (在我编辑答案以包含另一种技术后,我的答案不再“大体相同”。)
  • 与天真的解决方案相反,在访问列表或将项目添加到列表时是否会降低性能?
  • 编译器为您的匿名类型生成一个类,因此性能几乎与您创建一个普通类并使用它一样。
【解决方案2】:

您可以使用List&lt;dynamic&gt;

 var myList = new List<dynamic>();
 myList.Add(new {Tank = new Tank(), AttackingTank = new Tank()});

 Console.WriteLine("AttackingTank: {0}", myList[0].AttackingTank);

【讨论】:

  • 虽然这将允许您添加项目,但它不允许您将引用分配给匿名列表。
【解决方案3】:

这是一个技巧:

var myList = Enumerable.Empty<int>()
    .Select(dummy => new { AttackingTank = default(Tank), TargetTank = default(Tank), })
    .ToList();

如果Tank 是类类型,你可以写(Tank)null 而不是default(Tank)。你也可以使用你手头的一些Tank 实例。


编辑:

或者:

var myList = Enumerable.Repeat(
    new { AttackingTank = default(Tank), TargetTank = default(Tank), },
    0).ToList();

如果你创建一个泛型方法,你就不必使用Enumerable.Empty。它可以是这样的:

static List<TAnon> GetEmptyListWithAnonType<TAnon>(TAnon dummyParameter)
{
    return new List<TAnon>();
}

它是用 TAnon 从用法推断出来的,当然,如:

var myList = GetEmptyListWithAnonType(new { AttackingTank = default(Tank), TargetTank = default(Tank), });

【讨论】:

    【解决方案4】:

    值得注意的是,最后,有可能使用这样的语法。它已在 C# 7.0 中引入

    var tanks = new List<(Tank AttackingTank, Tank TargetTank)>();
    
    (Tank, Tank) tTank = (new Tank(), new Tank());
    tanks.Add(tTank);
    
    var a = tanks[0].AttackingTank;
    var b = tanks[0].TargetTank;
    

    【讨论】:

    • 我喜欢这个并在我的应用程序中使用它,但是这种类型的语法叫什么?我喜欢对它做一些额外的研究。
    • @MikeBarlow-BarDev 微软博客将其命名为元组类型和元组文字blogs.msdn.microsoft.com/dotnet/2017/03/09/…
    【解决方案5】:

    你可以有一个通用方法,它接受 params 并返回它:

    public static IList<T> ToAnonymousList<T>(params T[] items)
    {
      return items;
    }
    

    所以现在你可以说:

    var list=ToAnonymousList
    (
      new{A=1, B=2},
      new{A=2, B=2}
    );
    

    【讨论】:

    • 我想在一个方法中返回列表变量。该列表是 IList 但 T 未知。
    【解决方案6】:

    ExpandoObject 怎么样?

    dynamic tuple = new ExpandoObject(); 
    tuple.WhatEverYouWantTypeOfTank = new Tank(); // Value of any type
    

    编辑:

    dynamic tuple = new ExpandoObject();
    tuple.AttackingTank = new Tank();
    tuple.TargetTank = new Tank();
    
    var mylist = new List<dynamic> { tuple };
    
    //access to items
    Console.WriteLine(mylist[0].AttackingTank);
    

    【讨论】:

    • 您的答案仅涉及动态创建类型以替换 Tuple,OP 并没有真正遇到问题。他们需要知道如何处理泛型列表中的动态类型。在你的答案中解决这个问题,你很高兴(当然,我已经回答了;))
    • @HackedByChinese,好吧,我添加了一些编辑......另外我不确定你的代码是否可以工作......不支持这种动态对象初始化(new {Tank = new Tank(), AttackingTank = new Tank()})。
    • dynamic 适用于匿名类型,前提是您遵守匿名类型的一般限制。例如:如果 OP 要将结果列表作为方法的一部分返回,那么在使用其他程序集的结果时可能会出现问题(因为我相信匿名类型是内部的)。在这种情况下,您的 ExpandoObject 将是必要的,但是此时它会变得静音,因为 OP 应该使用适当的元组类来更好地表达 API。
    【解决方案7】:

    甚至更简单

            var tupleList = (
               new[] {
                    new { fruit = "Tomato", colour = "red"},
                    new { fruit = "Tomato", colour = "yellow"},
                    new { fruit = "Apple", colour = "red"},
                    new { fruit = "Apple", colour = "green"},
                    new { fruit = "Medlar", colour = "russet"}
                }).ToList();
    

    其中失去了静态功能。

    【讨论】:

      【解决方案8】:

      只是在这里添加一个更方便的位。我有时会使用亚历克斯的答案,但是当我需要它时试图追踪它,这让我有点发疯,因为它并不明显(我发现自己在搜索“新 {”)。

      所以我添加了以下小静态方法(我希望我可以将其设为扩展方法,但是是什么类型的?):

      public static List<T> CreateEmptyListOf<T>(Func<T> itemCreator)
      {
          return Enumerable
              .Empty<object>()
              .Select(o => itemCreator())
              .ToList();
      }
      

      这将每次我需要此模式时不同的部分与相同的部分隔离开来。我这样称呼它:

      var emptyList = Ext.CreateEmptyListOf(() => new { Name = default(string), SomeInt = default(int) });
      

      【讨论】:

      • 关于您的问题:您可以将方法主体替换为return new List&lt;T&gt;();。现在您不再需要Func&lt;T&gt;,而只需要T,因此您可以将参数定义更改为this T itemSample。瞧,任何类型的扩展方法。
      • 是的,这也有效。它确实有一个(可能无关紧要的)副作用:您必须实际创建一个实例才能创建列表。在代码中看到两者都有点令人困惑。使用你的方法看起来像这样:var emptyList = new { Name = default(string), SomeInt = default(int) }.CreateEmptyListOf(); 我不喜欢;当我不是真的时,看起来我正在创造一些东西。但这实际上只是一种意见。如果有一个内置的扩展程序或操作符,那真的是最好的了。
      • 两个代码都在堆上创建一个对象:委托或存根匿名对象。最好的办法是使用真正的类,并将匿名类的使用保持在隐式或显式 LINQ 链中。
      【解决方案9】:

      您可以创建对象列表:

      List<object> myList = new List<object>();
      

      然后将匿名类型添加到您的列表中:

      myList.Add(new {Name = "me", phone = 123});
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-04-06
        • 1970-01-01
        • 2010-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多