为什么我可以像在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()
调用,即使在只读字段上也是如此。