Почему я могу инициализировать список как массив в C #? - PullRequest
130 голосов
/ 13 января 2012

Сегодня я с удивлением обнаружил, что в C # я могу сделать:

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

Почему я могу это сделать? Какой конструктор называется? Как я могу сделать это с моими собственными классами? Я знаю, что это способ инициализации массивов, но массивы - это элементы языка, а списки - простые объекты ...

Ответы [ 6 ]

182 голосов
/ 13 января 2012

Это часть синтаксиса инициализатора коллекции в .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 )

11 голосов
/ 13 января 2012

Это так называемый синтаксический сахар .

List<T> - это «простой» класс, но компилятор применяет к нему особый режим, чтобы облегчить вашу жизнь.

Это так называемый инициализатор коллекции .Вам необходимо реализовать метод IEnumerable<T> и Add.

8 голосов
/ 13 января 2012

Согласно спецификации C # версии 3.0"Объект коллекции, к которому применяется инициализатор коллекции, должен относиться к типу, который реализует System.Collections.Generic.ICollection ровно для одной T."

Однако эта информация на момент написания статьи была неточной;см. пояснения Эрика Липперта в комментариях ниже.

6 голосов
/ 21 октября 2013

Еще одна интересная вещь в инициализаторах коллекций заключается в том, что вы можете иметь несколько перегрузок метода 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, тип возвращаемого значения может быть любым.

6 голосов
/ 13 января 2012

Это работает благодаря инициализаторам коллекции , которые в основном требуют, чтобы коллекция реализовала метод Add, и которые сделают всю работу за вас.

0 голосов
/ 16 марта 2016

Массив, подобный синтаксису, превращается в серию вызовов 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(), которые абсолютно допустимы даже для поля только для чтения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...