一个匿名类的通用列表

481

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
请注意,在数组中所有对象的类型必须相同。偶尔您可能需要进行强制转换,特别是对于空值 new[] { new{ Id = (int?)null, Name = "Foo" }, new { Id = (int?)1, Name = "Foo" }} - AaronLS
1
匿名类型被设计为临时存储使用,在大多数情况下,您会在LINQ选择语句中创建它们,使用Select(i=>new {i.ID,i.Name}); 这将返回正确类型的IEnumerable。如果您将while子句重新定义为LINQ.Where语句,则不应再需要列表,如果确实需要,可以随后调用ToList。 - MikeT
22个回答

491
你可以这样做:
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);

你明白我的意思 :)
需要翻译的内容已完成。

38
那会得到一个数组,而不是List<T>。 - Jon Skeet
1
你使用的是哪个版本?这是我得到的编译器错误。错误1:'System.Array'不包含'ToList'的定义,也没有接受类型为'System.Array'的第一个参数的扩展方法'ToList'(是否缺少using指令或程序集引用?) - DHornpout
27
你的文件顶部有没有写上 "using System.Linq;"?ToList 是 LINQ 操作符。 - Jon Skeet
6
明白了,需要加上 "using System.Linq"。谢谢。 - DHornpout
3
在Visual Studio中,智能感知对于揭示缺少的引用程序集(包括引用的扩展方法和类型)似乎存在不一致之处。 - LOAS
显示剩余3条评论

126

这里是答案。

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

14
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

80

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

如果您正在寻找一个空的通用类型列表,请使用选择对元组列表进行操作以制作空列表。不会实例化任何元素。

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

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

2
谢谢您的想法。一个建议是,如果您使用 Enumerable.Empty<object>().Select(o=>definition).ToList(),则可以避免分配虚拟列表。 - BrainStorm.exe
不错!这应该是被接受的答案。 - Muhammad Qasim

55

不完全正确,但你可以使用 List<object> ,这样会起作用。然而, list[0].Id 将无法运行。

在C# 4.0中,在运行时使用 List<dynamic> 可以实现此功能,但是你将无法获得 IntelliSense。


然而,从强类型的角度来看,您将无法在列表中的项目上获得编译器智能感支持。 - Joel Coehoorn
4
我并没有说这是一个“好”主意,而是说它是可能的 :-) 例如,存储来自Ruby的对象可能会有需求。 - Jeff Moser
3
但在这些情况下,源类型毕竟是动态的,因此对于匿名类型使用List<dynamic>是没有意义的。 - Dykam
1
非常有帮助。特别是在匿名项添加到列表之前必须定义列表时。 - Karlth

42

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

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

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>即可。 - Martin

24

我通常使用以下方式;主要是因为这样你就可以从一个空列表开始。

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),并将你在选择语句中的第一个对象作为第一个对象。 - Matthew M.
2
你不需要从空列表开始 - 在你知道第一个项目是什么的情况下(就像在示例中一样),那么你的注释是正确的。然而,很多时候我用它来解析中间文件/数据源,在使用LINQ投影场景之前不会访问第一个真实项目(因此不需要考虑跳过第一个记录)。 - Rostov

22

你可以在你的代码中实现这个。

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

13

我在几个答案中检查了 IL。 这段代码高效地提供了一个空List:

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

1
有什么理由拒绝我的编辑?我的版本返回的是List,正好符合OP的要求,而下面的答案返回的是IEnumerable - Necronomicron
1
我更喜欢这种方法,甚至更接近于这个答案new object[] { }.Select(o => new { Id = default(int), Name = default(string) }).ToList() - palswim

12
在最新的4.0版本中,可以像下面这样使用动态方式。
var list = new List<dynamic>();
        list.Add(new {
            Name = "Damith"
    });
        foreach(var item in list){
            Console.WriteLine(item.Name);
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接