为什么我可以在C#中像数组一样初始化List?


131

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

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

我为什么可以这样做?什么叫构造函数?我如何在自己的课堂上做到这一点?我知道这是初始化数组的方法,但是数组是语言项,列表是简单的对象...


1
这个问题可能会有所帮助:stackoverflow.com/questions/1744967/...
鲍勃·考夫曼

10
太棒了吧?您也可以执行非常相似的代码来初始化字典:{ { "key1", "value1"}, { "key2", "value2"} }
danludwig 2012年

从另一个角度来看,这种类似于初始化语法的数组使我们想起a的基础数据类型List<int>实际上只是一个数组。我讨厌C#团队,因为他们没有给ArrayList<T>它命名,这听起来是如此明显和自然。
RBT

Answers:


183

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

  • 实现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()方法不必采用单个项目,例如,用于的Add()方法可以Dictionary<TKey, TValue>采用两个项目:

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>)并创建一个或多个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();
    }
}

然后,您可以像BCL集合一样使用它:

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

    // ...
}

(有关更多信息,请参见MSDN


29
好的答案,尽管我注意到在第一个示例中说“完全相同”并不十分准确。它与完全相同。List<int> temp = new List<int>(); temp.Add(1); ... List<int> a = temp; 也就是说,a直到所有添加被调用之后,变量才会初始化。否则做类似的事情List<int> a = new List<int>() { a.Count, a.Count, a.Count };是疯狂的。
埃里克·利珀特

1
@ user606723:List<int> a; a = new List<int>() { a.Count };和之间没有任何真正的区别List<int> a = new List<int>() { a.Count };
Joren 2012年

4
@Joren:你是对的。实际上,C#规范指出T x = y;与相同T x; x = y;,这一事实可能导致某些奇怪的情况。例如,int x = M(out x) + x;完全合法是因为int x; x = M(out x) + x;合法。
埃里克·利珀特

1
@JamesMichaelHare不需要实现IEnumerable<T>;非泛型IEnumerable足以允许使用集合初始化程序语法。
phoog,2012年

2
如果您正在使用某种实现IDisposable的集合,那么Eric的观点很重要。using (var x = new Something{ 1, 2 })如果其中一个Add调用失败,则不会处理该对象。
porges 2012年

11

所谓的语法糖

List<T> 是“简单”类,但是编译器对此类进行了特殊处理,以使您的生活更轻松。

这就是所谓的集合初始化器。您需要实现IEnumerable<T>Add方法。


8

根据C#3.0版规范 “应用集合初始值设定项的集合对象必须是为System.Collections.Generic.ICollection精确实现一个T的类型。”

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


10
该页面不正确。感谢您引起我的注意。那一定是一些发行前的初步文档,但从未被删除。原始设计需要ICollection,但这不是我们确定的最终设计。
埃里克·利珀特

1
谢谢你的澄清。
Olivier Jacot-Descombes 2012年


6

关于集合初始化程序的另一个很酷的事情是,您可以有多个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
};

它调用正确的重载。另外,它只查找具有name的方法Add,返回类型可以是任何东西。


0

像语法这样的数组将在一系列 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()调用,即使在只读字段上也完全合法。

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.