【问题标题】:A generic list of anonymous class匿名类的通用列表
【发布时间】:2009-03-04 22:08:47
【问题描述】:

在 C# 3.0 中,您可以使用以下语法创建匿名类

var o = new { Id = 1, Name = "Foo" };

有没有办法将这些匿名类添加到通用列表中?

例子:

var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };

List<var> list = new List<var>();
list.Add(o);
list.Add(o1);

另一个例子:

List<var> list = new List<var>();

while (....)
{
    ....
    list.Add(new {Id = x, Name = y});
    ....
}

【问题讨论】:

  • 请注意,数组中所有对象的类型必须相同。您可能很少需要帮助进行演员阵容,尤其是对于空值new[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
  • 匿名类型被设计用作临时存储,在大多数情况下,您可以在 LINQ 选择语句中使用 Select(i=>new {i.ID,i.Name}); 创建它们。如果您将 while 子句重新定义为 LINQ.Where 语句,则将返回正确类型的 IEnumerable ,您永远不需要该列表,如果需要,您只需调用 ToList 即可

标签: c# .net generics anonymous-types


【解决方案1】:

你可以这样做:

var list = new[] { o, o1 }.ToList();

有很多方法可以给这只猫剥皮,但基本上它们都会在某个地方使用类型推断——这意味着你必须调用一个泛型方法(可能作为扩展方法)。另一个例子可能是:

public static List<T> CreateList<T>(params T[] elements)
{
     return new List<T>(elements);
}

var list = CreateList(o, o1);

你明白了:)

【讨论】:

  • @DHornpout:这将给出一个数组,而不是一个 List
  • 你用的是什么版本?这是我得到的编译器错误 错误 1 ​​'System.Array' 不包含 'ToList' 的定义,并且找不到接受类型为 'System.Array' 的第一个参数的扩展方法 'ToList'(您是否缺少使用指令还是程序集引用?)
  • @DHornpout:你有“使用 System.Linq;”吗?在文件的顶部? ToList 是一个 LINQ 运算符。
  • 知道了。需要包含“使用 System.Linq”。谢谢。
  • 在 Visual Studio 中似乎存在不一致,智能感知在发现缺少包含引用扩展方法(与引用类型相同)的程序集方面没有多大帮助。
【解决方案2】:

这就是答案。

string result = String.Empty;

var list = new[]
{ 
    new { Number = 10, Name = "Smith" },
    new { Number = 10, Name = "John" } 
}.ToList();

foreach (var item in list)
{
    result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}

MessageBox.Show(result);

【讨论】:

  • Dutt,你的代码应该在没有 .ToList() 结尾的情况下工作。
  • 好吧,现在我们需要一个用 select 语句替换新 {} 行的示例。 var list = sourceList.Select( o => new { o.ModelId, o.PartNumber, o.Quantity }).ToList();
  • @towpse 有什么解决办法吗?
  • @Dutt,如果我使用返回 List 的方法(函数)的任何示例?
  • 现在有方法string.Join和字符串插值,所以不用foreachFormat了。
【解决方案3】:

有很多方法可以做到这一点,但这里的一些响应是创建一个包含垃圾元素的列表,这需要您清除该列表。

如果您正在寻找泛型类型的空列表,请针对元组列表使用 Select 来创建空列表。不会实例化任何元素。

这是创建空列表的单行代码:

 var emptyList = new List<Tuple<int, string>>()
          .Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();

然后你可以使用你的泛型类型添加到它:

 emptyList.Add(new { Id = 1, Name = "foo" });
 emptyList.Add(new { Id = 2, Name = "bar" });

作为替代方案,您可以执行以下操作来创建空列表(但是,我更喜欢第一个示例,因为您也可以将它用于填充的元组集合):

 var emptyList = new List<object>()
          .Select(t => new { Id = default(int), Name = default(string) }).ToList();   

【讨论】:

  • 感谢您的想法。一个建议,如果你使用Enumerable.Empty&lt;object&gt;().Select(o=&gt;definition).ToList(),你可以避免分配一个虚拟列表
  • 不错的一个!这应该是公认的答案
【解决方案4】:

不完全是,但你可以说List&lt;object&gt;,一切都会奏效。但是,list[0].Id 不起作用。

这将在 C# 4.0 中通过List&lt;dynamic&gt; 工作在运行时,也就是说您不会获得 IntelliSense。

【讨论】:

  • 它不是强类型的,但从某种意义上说,您不会对列表中的项目提供编译器智能感知支持。
  • 我并没有说这是一个伟大的想法,但它是可能的 :-) 例如,如果从 Ruby 中存储对象,则可能需要。
  • 但在这些情况下,源类型毕竟是动态的,将 List 用于匿名类型是没有意义的。
  • 非常有帮助。特别是如果必须在将匿名项目添加到其中之前定义列表。
【解决方案5】:

我通常使用以下内容;主要是因为您然后“开始”了一个空列表。

var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.

最近,我一直是这样写的:

var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );

使用 repeat 方法也可以:

var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });

..它为您提供了已添加第一项的初始列表。

【讨论】:

  • 你不需要从一个空列表开始 - 你可以做 Range(0,1) 并让你在 select 语句中的第一个对象成为..第一个对象。
  • 您不需要从一个空列表开始——如果您知道第一项是什么(如示例中所示),那么您的评论就对了。很多时候,尽管我使用它来解析中间文件/数据源并且在 LINQ 投影场景中使用它之前不访问第一个真正的项目(因此不需要考虑跳过第一条记录)。
【解决方案6】:

我猜

List<T> CreateEmptyGenericList<T>(T example) {
    return new List<T>();
}

void something() {
    var o = new { Id = 1, Name = "foo" };
    var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}

会起作用的。

你也可以考虑这样写:

void something() {
    var String = string.Emtpy;
    var Integer = int.MinValue;
    var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}

【讨论】:

  • 是的,这个解决方案将帮助解​​决初始化匿名数组的问题。谢谢。
  • 在方法名后面加一点
【解决方案7】:

您可以在代码中执行此操作。

var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });

【讨论】:

    【解决方案8】:

    如果您使用的是 C# 7 或更高版本,则可以使用 tuple types 代替匿名类型。

    var myList = new List<(int IntProp, string StrProp)>();
    myList.Add((IntProp: 123, StrProp: "XYZ"));
    

    【讨论】:

      【解决方案9】:

      在最新的4.0版本中,可以像下面这样使用动态

      var list = new List<dynamic>();
              list.Add(new {
                  Name = "Damith"
          });
              foreach(var item in list){
                  Console.WriteLine(item.Name);
              }
          }
      

      【讨论】:

        【解决方案10】:

        我检查了几个答案的 IL。此代码有效地提供了一个空列表:

            using System.Linq;
            …
            var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
        

        【讨论】:

        • 有什么理由拒绝我的编辑?以下答案返回IEnumerable,而我的版本返回List,正是OP所要求的。
        • 我更喜欢这种方法,甚至更接近this answer:new object[] { }.Select(o =&gt; new { Id = default(int), Name = default(string) }).ToList()
        【解决方案11】:

        您可以创建动态列表。

        List<dynamic> anons=new List<dynamic>();
        foreach (Model model in models)
        {
           var anon= new
           {
              Id = model.Id,
              Name=model.Name
           };
           anons.Add(anon);
        }
        

        “动态”由添加的第一个值初始化。

        【讨论】:

          【解决方案12】:

          这是我的尝试。

          List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }}; 
          

          当我为自定义类型创建一个匿名列表编写类似的东西时,我想出了这个。

          【讨论】:

            【解决方案13】:

            我很惊讶没有人建议使用集合初始值设定项。这种方式只能在创建列表时添加对象,因此名称然而它似乎是最好的方式。无需创建数组然后将其转换为列表。

            var list = new List<dynamic>() 
            { 
                new { Id = 1, Name = "Foo" }, 
                new { Id = 2, Name = "Bar" } 
            };
            

            您始终可以使用object 而不是dynamic,但尝试以真正通用的方式保留它然后dynamic 更有意义。

            【讨论】:

            • 动态和对象在运行时和编译时被区别对待。如果您尝试使用动态调用“list[0].Id”,则不会出现编译错误。使用对象是不可能的。 Object 是继承所有类的基类,一种预先确定的“类型”。另一方面,动态是代码执行前的“未确定类型”。
            【解决方案14】:

            而不是这个:

            var o = new { Id = 1, Name = "Foo" }; 
            var o1 = new { Id = 2, Name = "Bar" }; 
            
            List <var> list = new List<var>(); 
            list.Add(o); 
            list.Add(o1);
            

            你可以这样做:

            var o = new { Id = 1, Name = "Foo" }; 
            var o1 = new { Id = 2, Name = "Bar" }; 
            
            List<object> list = new List<object>(); 
            list.Add(o); 
            list.Add(o1);
            

            但是,如果您尝试在另一个范围内执行类似的操作,您将收到编译时错误,尽管它在运行时有效:

            private List<object> GetList()
            { 
                List<object> list = new List<object>();
                var o = new { Id = 1, Name = "Foo" }; 
                var o1 = new { Id = 2, Name = "Bar" }; 
                list.Add(o); 
                list.Add(o1);
                return list;
            }
            
            private void WriteList()
            {
                foreach (var item in GetList()) 
                { 
                    Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine); 
                }
            }
            

            问题是只有 Object 的成员在运行时可用,尽管智能感知会显示属性 idname

            在 .net 4.0 中,一个解决方案是在上面的代码中使用关键字 dynamic 而不是 object

            另一种解决方案是使用反射来获取属性

            using System;
            using System.Collections.Generic;
            using System.Reflection;
            
            namespace ConsoleApplication1
            {
                class Program
                {
                    static void Main(string[] args)
                    {
                        Program p = new Program();
                        var anonymous = p.GetList(new[]{
                            new { Id = 1, Name = "Foo" },       
                            new { Id = 2, Name = "Bar" }
                        });
            
                        p.WriteList(anonymous);
                    }
            
                    private List<T> GetList<T>(params T[] elements)
                    {
                        var a = TypeGenerator(elements);
                        return a;
                    }
            
                    public static List<T> TypeGenerator<T>(T[] at)
                    {
                        return new List<T>(at);
                    }
            
                    private void WriteList<T>(List<T> elements)
                    {
                        PropertyInfo[] pi = typeof(T).GetProperties();
                        foreach (var el in elements)
                        {
                            foreach (var p in pi)
                            {
                                Console.WriteLine("{0}", p.GetValue(el, null));
                            }
                        }
                        Console.ReadLine();
                    }
                }
            }
            

            【讨论】:

              【解决方案15】:

              这是另一种创建匿名类型列表的方法,它允许您从一个空列表开始,但仍然可以访问 IntelliSense。

              var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();
              

              如果您想保留第一项,只需在字符串中输入一个字母即可。

              var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
              

              【讨论】:

              • 使用字符串作为数组初始值设定项可能有效,但这种做法非常糟糕
              • 我想说,上述大多数答案都不是特别“好的”做法,但由于问题的性质,这是一种既定的做法。匿名类型并不是真正设计成以这种方式工作的。我很好奇为什么我的方法比其他方法“更糟糕”?我错过了什么?
              • 你是对的,因为问题是要求一些本身不好的做法,所以不可能有好的做法答案,但是因为你使用字符串来生成一个字符数组,然后将其转换为用户想要的数组,它们是不相关的类型,更不用说产生多余的装箱和拆箱,效率低下
              【解决方案16】:

              你可以这样做:

              var o = new { Id = 1, Name = "Foo" };
              var o1 = new { Id = 2, Name = "Bar" };
              
              var array = new[] { o, o1 };
              var list = array.ToList();
              
              list.Add(new { Id = 3, Name = "Yeah" });
              

              对我来说这似乎有点“hacky”,但它确实有效 - 如果你真的需要一个列表并且不能只使用匿名数组。

              【讨论】:

                【解决方案17】:
                var list = new[]{
                new{
                FirstField = default(string),
                SecondField = default(int),
                ThirdField = default(double)
                }
                }.ToList();
                list.RemoveAt(0);
                

                【讨论】:

                  【解决方案18】:

                  这是一个老问题,但我想我会在 C# 6 中给出答案。我经常需要设置可以在代码中轻松输入的测试数据作为元组列表。使用几个扩展函数,就可以拥有这种漂亮、紧凑的格式,而无需在每个条目上重复名称。

                  var people= new List<Tuple<int, int, string>>() {
                      {1, 11, "Adam"},
                      {2, 22, "Bill"},
                      {3, 33, "Carol"}
                  }.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });
                  

                  这给出了一个 IEnumerable - 如果你想要一个可以添加的列表,那么只需添加 ToList()。

                  魔力来自于自定义扩展添加元组的方法,如https://stackoverflow.com/a/27455822/4536527 所述。

                  public static class TupleListExtensions    {
                      public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
                              T1 item1, T2 item2)       {
                          list.Add(Tuple.Create(item1, item2));
                      }
                  
                      public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
                              T1 item1, T2 item2, T3 item3) {
                          list.Add(Tuple.Create(item1, item2, item3));
                      }
                  
                  // and so on...
                  

                  }

                  我唯一不喜欢的是类型与名称是分开的,但如果你真的不想创建一个新类,那么这种方法仍然可以让你拥有可读的数据。

                  【讨论】:

                    【解决方案19】:

                    对于第二个示例,您必须初始化一个新的List&lt;T&gt;,一个想法是创建一个匿名列表,然后将其清除。

                    var list = new[] { o, o1 }.ToList();
                    list.Clear();
                    
                    //and you can keep adding.
                    while (....)
                    {
                        ....
                        list.Add(new { Id = x, Name = y });
                        ....
                    }
                    

                    或者作为扩展方法,应该更简单:

                    public static List<T> GetEmptyListOfThisType<T>(this T item)
                    {
                        return new List<T>();
                    }
                    
                    //so you can call:
                    var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();
                    

                    或者可能更短,

                    var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
                    

                    【讨论】:

                      【解决方案20】:

                      源自this answer,我想出了两种可以完成任务的方法:

                          /// <summary>
                          /// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
                          /// for the needed type inference. This overload is for when you don't have an instance of the anon class
                          /// and don't want to make one to make the list.
                          /// </summary>
                          /// <typeparam name="T"></typeparam>
                          /// <param name="definition"></param>
                          /// <returns></returns>
                      #pragma warning disable RECS0154 // Parameter is never used
                          public static List<T> CreateListOfAnonType<T>(Func<T> definition)
                      #pragma warning restore RECS0154 // Parameter is never used
                          {
                              return new List<T>();
                          }
                          /// <summary>
                          /// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
                          /// only used for the needed type inference. This overload is for when you do have an instance of the anon
                          /// class and don't want the compiler to waste time making a temp class to define the type.
                          /// </summary>
                          /// <typeparam name="T"></typeparam>
                          /// <param name="definition"></param>
                          /// <returns></returns>
                      #pragma warning disable RECS0154 // Parameter is never used
                          public static List<T> CreateListOfAnonType<T>(T definition)
                      #pragma warning restore RECS0154 // Parameter is never used
                          {
                              return new List<T>();
                          }
                      

                      你可以使用类似的方法

                      var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
                      //or
                      var existingAnonInstance = new { Id = 59, Name = "Joe" };
                      var otherEmptyList = CreateListOfAnonType(existingAnonInstance);
                      

                      This answer也有类似的想法,但是我做了这些方法之后才看到。

                      【讨论】:

                        【解决方案21】:

                        试试这个:

                        var result = new List<object>();
                        
                        foreach (var test in model.ToList()) {
                           result.Add(new {Id = test.IdSoc,Nom = test.Nom});
                        }
                        

                        【讨论】:

                        • 匿名类型的列表在哪里?
                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多