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

Ign*_*cia 131 .net c# initialization list

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

List<int> a = new List<int> { 1, 2, 3 };
Run Code Online (Sandbox Code Playgroud)

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

Jam*_*are 183

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

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

  • 它有一个名为的方法 Add(...)

发生的是调用默认构造函数,然后Add(...)为初始化程序的每个成员调用.

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

List<int> a = new List<int> { 1, 2, 3 };
Run Code Online (Sandbox Code Playgroud)

List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;
Run Code Online (Sandbox Code Playgroud)

可以拨打一个可选的构造,如果你想要的,例如,以防止过度大小的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, }
Run Code Online (Sandbox Code Playgroud)

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

var grades = new Dictionary<string, int>
    {
        { "Suzy", 100 },
        { "David", 98 },
        { "Karen", 73 }
    };
Run Code Online (Sandbox Code Playgroud)

大致相同:

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

因此,要将此添加到您自己的类中,您所需要做的就是实现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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 很好的答案,虽然我注意到在你的第一个例子中说"完全相同"并不准确.它与`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};`这样做是很合理的. (29认同)
  • @Joren:你是对的; 事实上,C#规范声明`T x = y;`与`T x相同; x = y;`,这个事实可能导致一些奇怪的情况.例如,`int x = M(out x)+ x;`是完全合法的,因为`int x; x = M(out x)+ x;`是合法的. (4认同)
  • 如果你使用某种实现IDisposable的集合,Eric的观点很重要.`using(var x = new Something {1,2})`如果其中一个`Add`调用失败,则不会处理该对象. (2认同)

Kri*_*izz 11

它就是所谓的语法糖.

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

这个是所谓的集合初始化器.你需要实现IEnumerable<T>Add方法.


Oli*_*bes 8

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

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

  • 该页面不准确.谢谢你引起我的注意.这必须是一些初步的预发布文档,不知何故从未被删除.最初的设计是要求ICollection,但这不是我们确定的最终设计. (10认同)

vc *_* 74 6

它的工作原理归功于集合初始化程序,它基本上要求集合实现Add方法,并且可以为您完成工作.


naw*_*fal 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
};
Run Code Online (Sandbox Code Playgroud)

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