为什么我可以像在C#中的数组初始化列表?

今天我惊讶地发现,在C#中我可以这样做:

List<int> a = new List<int> { 1, 2, 3 }; 

我为什么要这样做? 什么构造函数被调用? 我怎样才能用我自己的课程呢? 我知道这是初始化数组的方式,但数组是语言项目和列表是简单的对象…

这是.NET中的集合初始化器语法的一部分。 您可以在创build的任何集合上使用此语法,只要:

  • 它实现IEnumerable (最好IEnumerable<T>

  • 它有一个名为Add(...)

会发生什么情况是调用默认的构造函数,然后为初始值设定项的每个成员调用Add(...)

因此,这两个块大致相同:

 List<int> a = new List<int> { 1, 2, 3 }; 

 List<int> temp = new List<int>(); temp.Add(1); temp.Add(2); temp.Add(3); List<int> a = temp; 

如果你愿意,你可以调用一个可选的构造函数,例如防止在增长期间过大的List<T>等等:

 // Notice, calls the List constructor that takes an int arg // for initial capacity, then Add()'s three items. List<int> a = new List<int>(3) { 1, 2, 3, } 

请注意, Add()方法不需要单个项目,例如Dictionary<TKey, TValue>Add()方法需要两个项目:

 var grades = new Dictionary<string, int> { { "Suzy", 100 }, { "David", 98 }, { "Karen", 73 } }; 

大致相同:

 var temp = new Dictionary<string, int>(); temp.Add("Suzy", 100); temp.Add("David", 98); temp.Add("Karen", 73); var grades = temp; 

所以,为了将这个添加到你自己的类中,你所需要做的就是实现IEnumerable (再次,最好是IEnumerable<T> )并创build一个或多个Add()方法:

 public class SomeCollection<T> : IEnumerable<T> { // implement Add() methods appropriate for your collection public void Add(T item) { // your add logic } // implement your enumerators for IEnumerable<T> (and IEnumerable) public IEnumerator<T> GetEnumerator() { // your implementation } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

那么你可以像BCLcollections一样使用它:

 public class MyProgram { private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 }; // ... } 

(有关更多信息,请参阅MSDN )

这就是所谓的句法糖 。

List<T>是“简单”类,但是编译器给它一个特殊的处理,以使你的生活更轻松。

这个是所谓的集合初始值设定器 。 您需要实现IEnumerable<T>Add方法。

根据C#3.0版规范 “集合初始值设定项应用到的集合对象必须是实现System.Collections.Generic.ICollection的types。

但是,在撰写本文时,这些信息似乎是不准确的。 请参阅Eric Lippert在下面的评论中的说明。

它的工作原理感谢收集初始值设定项 ,它基本上要求收集项实现一个Add方法,并且会为您完成这项工作。

关于集合初始化器的另一个很酷的事情是,你可以有多个Add方法的重载,你可以在同一个初始化器中调用它们! 例如这个工程:

 public class MyCollection<T> : IEnumerable<T> { public void Add(T item, int number) { } public void Add(T item, string text) { } public bool Add(T item) //return type could be anything { } } var myCollection = new MyCollection<bool> { true, { false, 0 }, { true, "" }, false }; 

它调用正确的重载。 此外,它只查找名称为Add的方法,返回types可以是任何东西。

类似语法的数组在一系列的Add()调用中被转向。

为了在更有趣的例子中看到这个,考虑下面的代码,我做了两件有趣的事情,在C#中第一个非法的东西,1)设置一个只读属性,2)设置一个像初始化数组的列表。

 public class MyClass { public MyClass() { _list = new List<string>(); } private IList<string> _list; public IList<string> MyList { get { return _list; } } } //In some other method var sample = new MyClass { MyList = {"a", "b"} }; 

此代码将完美工作,虽然1)MyList是只读的,2)我设置了一个列表与数组初始化。

之所以这么做,是因为在作为对象初始化程序的一部分的代码中,编译器总是将任何类似于语法的语法都转换成一系列Add()调用,即使在只读字段上也是如此。