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

时间:2012-01-13 16:35:09

标签: c# .net list initialization

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

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

为什么我能这样做?调用什么构造函数?我怎么能用我自己的课程做到这一点?我知道这是初始化数组的方法,但数组是语言项,列表是简单的对象......

6 个答案:

答案 0 :(得分:182)

这是.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()的{​​{1}}方法需要两个项目:

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

然后就像BCL集合那样使用它:

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

(有关详细信息,请参阅MSDN

答案 1 :(得分:11)

它被称为syntactic sugar

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

这个被称为collection initializer。您需要实施IEnumerable<T>Add方法。

答案 2 :(得分:8)

根据C# Version 3.0 Specification“应用集合初始化程序的集合对象必须是一个实现System.Collections.Generic.ICollection的类型,只有一个T.”

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

答案 3 :(得分:6)

这要归功于collection initializers,它基本上要求集合实现Add方法,并为您完成工作。

答案 4 :(得分: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
};

它调用正确的重载。此外,它只查找名为Add的方法,返回类型可以是任何内容。

答案 5 :(得分:0)

类似语法的数组正在进行一系列Add()次调用。

要在更有趣的示例中看到这一点,请考虑以下代码,其中我执行两个有趣的事情,这些事情在C#中首先是非法的,1)设置readonly属性,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()调用,这些调用即使在readonly上也是完全合法的字段。