匿名类的通用列表


416

在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});
    ....
}

2
请注意,所有对象在数组中的键入必须相同。您很少需要帮助演员,尤其是对于nullnew[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }}
AaronLS

1
匿名类型旨在用作临时存储,在大多数情况下,您将使用Select(i => new {i.ID,i.Name})在LINQ select语句中创建它们。如果将while子句重新定义为LINQ,则它将返回正确类型的IEnumerable。在where语句中,您永远不需要列表,如果您这样做,则可以直接在其上调用ToList
MikeT

Answers:


427

您可以这样做:

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

你明白了:)


32
@DHornpout:这将提供一个数组,而不是List <T>。
乔恩·斯基特

23
@DHornpout:您是否“正在使用System.Linq”?在文件的顶部?ToList是LINQ运算符。
乔恩·斯基特

5
知道了。需要包含“ using System.Linq”。谢谢。
DHornpout

2
在Visual Studio中似乎确实存在不一致的地方,即intellisense在发现具有引用扩展方法(与引用类型相同)的程序集的缺失包含方面没有更多帮助。
LOAS,2012年

3
这个人无处不在,今天搜寻了8个问题,他回答了7个问题。
Kugan Kumar

109

这是答案。

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

12
达特(Dutt),您的代码应该在结尾没有.ToList()的情况下工作。
DHornpout

3
好的,很酷,现在我们需要一个用select语句替换新{}行的示例。var list = sourceList.Select(o => new {o.ModelId,o.PartNumber,o.Quantity})。ToList();
topwik

@towpse有什么解决方案吗?
Kiquenet

@Dutt,如果我使用返回List <T>的方法(函数),则为任何示例?
Kiquenet

现在有方法string.Join和字符串插值,因此无需使用foreachFormat
realsonic

61

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

如果要查找泛型类型的空列表,请使用针对元组列表的选择来创建空列表。没有元素将被实例化。

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

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

1
我很喜欢这种方式。谢谢保罗!可以使用Tuples永远是美好的一天!xD
Brady Liles

我喜欢这个。可以对要传递的对象进行某种具体的声明真是
太好了。– Morvael

很好,我刚刚写完代码,其中包括清除列表时间以进行重写
JoshBerke

谢谢你的主意。一个建议,如果使用,可以避免分配虚拟列表Enumerable.Empty<object>().Select(o=>definition).ToList()
BrainStorm.exe

45

不完全是,但是您可以说List<object>一切都会奏效。但是,list[0].Id将无法正常工作。

通过使用可以在C#4.0 中的运行时工作List<dynamic>,即您不会获得IntelliSense。


但是,从某种意义上说,它不是强类型的,因为您将对列表中的项目没有编译器的智能支持。
乔尔·科恩荷恩

31
我担心人们会动态地进行这种事情。
erikkallen

2
我并不是说这是个主意,但是有可能:-)例如,如果存储来自Ruby的对象,可能会需要。
杰夫·摩泽尔2009年

2
但是在那些情况下,源类型毕竟是动态的,对匿名类型使用List <dynamic>毫无意义。
Dykam,2009年

1
非常有帮助。特别是如果在添加匿名项目之前必须先定义列表。
卡尔斯,2014年

24

我猜

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

是的,此解决方案将帮助解​​决初始化匿名数组的问题。谢谢。
DHornpout

1
只需在方法名称后面加一个<T>即可。
马丁

21

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

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

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


2
您无需以空列表开头-您可以执行Range(0,1)并将select语句中的第一个对象设为第一个对象。
马修M.14年

1
您无需以空列表开头-如果您知道第一项是什么(如示例中所示),那么您的评论就对了。尽管很多时候我使用它来解析中间文件/数据源,但直到在LINQ投影方案中使用它之前,才访问第一个真实项目(因此无需考虑跳过第一个记录)。
罗斯托夫2014年

19

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

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

11

在最新版本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();

1
有什么理由拒绝我的编辑吗?遵循答案返回IEnumerable,而我的版本返回List,这完全是OP要求的。
Necronomicron 2015年

我更喜欢这种方法,甚至更接近这个答案new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList()
palswim

8

这是我的尝试。

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

当我写了类似的文章为自定义类型创建匿名列表时,我想到了这一点。


8

您可以创建一个动态列表。

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

“动态”通过添加的第一个值进行初始化。


7

代替这个:

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

问题是,尽管intellisense将显示属性idname,但是只有Object的成员在运行时可用。

在.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();
        }
    }
}

7

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

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

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

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

使用字符串作为数组初始值设定项可能有效,但非常不好的做法
MikeT 2014年

我要说的是,上述大多数答案并不是特别的“良好”做法,但是由于问题的性质,这是必然的。匿名类型并不是真正设计成这种方式。我很好奇为什么我的方法比其他方法更“糟糕”?我缺少什么?
Brackus 2014年

您是对的,因为问题是在要求某种本身是不好的做法,所以没有好的做法答案,但是因为您使用的是字符串来生成字符数组,然后将其转换为用户想要的数组,这是无关的类型,更不用说产生多余的装箱和拆箱了,效率不高
MikeT 2015年

5
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);

5

这是一个老问题,但是我想我应该填写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()。

魔术来自用于元组的自定义扩展Add方法,如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...

}

我唯一不喜欢的是类型与名称分开,但是如果您真的不想创建新类,则此方法仍将使您拥有可读数据。


4

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

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

您可以始终使用object而不是,dynamic但是尝试以一种真正的通用方式进行保留,这样dynamic会更有意义。


3

您可以这样操作:

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”,但它确实有效-如果您确实需要一个列表并且不能只使用匿名数组。


3

对于第二个示例,必须初始化一个new List<T>,一个想法是创建一个匿名列表,然后清除它。

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

2

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

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

1

这个答案得出,我想出了两种可以完成任务的方法:

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

这个答案有一个相似的想法,但是直到做出这些方法后我才看到它。



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.