2012-01-13 1 views
127

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

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

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

+1

Этот вопрос может быть полезным: http://stackoverflow.com/questions/1744967/in-c-is-there-a-way-to-write-custom-object-initializers-for-new-data- типы –

+10

Pretty awesome huh? Вы можете сделать очень похожий код для инициализации словарей: '{{" key1 "," value1 "}, {" key2 "," value2 "}}' – danludwig

+0

С другого представления этот массив, такой как синтаксис инициализации, дает нам напоминание о том, что базовые тип данных «Список ' на самом деле является массивом. Я ненавижу команду C# за то, что они не называют ее «ArrayList », которая звучит так очевидно и естественно. – RBT

ответ

180

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

+0

Hah, thanks :-) –

+29

Хороший ответ, хотя я отмечаю, что не совсем точно сказать «точно идентичный» в вашем первом примере. Он точно идентичен «List temp = new List (); temp.Add (1); ... List a = temp; 'То есть, переменная' a' не инициализируется до * после *, все вызовы добавляются. В противном случае было бы законно делать что-то вроде «Список a = новый список () {a.Count, a.Count, a.Count};' который является сумасшедшей задачей. –

+0

@EricLippert, 'List a = новый Список () {a.Count, a.Count, a.Count};' не является законным ни в одном случае, потому что в этой точке нет ссылки. Я думаю, что его первоначальный фрагмент был прекрасен. – user606723

11

Это так называемый syntactic sugar.

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

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

6

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

8

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

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

+10

Эта страница не является точной. Спасибо, что привлек это к моему вниманию. Это должна быть предварительная предварительная документация, которая никогда не удалялась. Первоначальный дизайн должен был требовать ICollection, но это не окончательный дизайн, на котором мы остановились. –

+1

Благодарим вас за разъяснение. –

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, тип возврата может быть любым.

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) я установить список с массивом инициализатором.

Причина, по которой это происходит, заключается в том, что в коде, который является частью объекта intializer, компилятор всегда превращает любой {} как синтаксис в ряд вызовов Add(), которые вполне законны даже в поле readonly.

Смежные вопросы