Dziś zdziwiło mnie, że w C # potrafię:
List<int> a = new List<int> { 1, 2, 3 };
Dlaczego mogę to zrobić? Jaki konstruktor się nazywa? Jak mogę to zrobić z własnymi zajęciami? Wiem, że to jest sposób na inicjalizację tablic, ale tablice to elementy języka, a listy to proste obiekty ...
c#
.net
list
initialization
Ignacio Soler Garcia
źródło
źródło
{ { "key1", "value1"}, { "key2", "value2"} }
List<int>
jest w rzeczywistości tylko tablicą. Nienawidzę zespołu C # za to, że nie nazywają go,ArrayList<T>
co brzmi tak oczywiste i naturalne.Odpowiedzi:
Jest to część składni inicjatora kolekcji w .NET. Tej składni możesz używać w każdej tworzonej kolekcji, o ile:
Wdraża
IEnumerable
(najlepiejIEnumerable<T>
)Ma metodę o nazwie
Add(...)
Dzieje się tak, gdy wywoływany jest domyślny konstruktor, a następnie
Add(...)
jest wywoływany dla każdego elementu członkowskiego inicjatora.Zatem te dwa bloki są z grubsza identyczne:
List<int> a = new List<int> { 1, 2, 3 };
I
List<int> temp = new List<int>(); temp.Add(1); temp.Add(2); temp.Add(3); List<int> a = temp;
Ty można nazwać alternatywną konstruktora, jeśli chcesz, na przykład, aby zapobiec nadmiernej doborze
List<T>
podczas uprawy, itp:// 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, }
Zwróć uwagę, że
Add()
metoda nie musi uwzględniać jednego elementu, na przykładAdd()
metoda dlaDictionary<TKey, TValue>
wymaga dwóch elementów:var grades = new Dictionary<string, int> { { "Suzy", 100 }, { "David", 98 }, { "Karen", 73 } };
Jest mniej więcej identyczny z:
var temp = new Dictionary<string, int>(); temp.Add("Suzy", 100); temp.Add("David", 98); temp.Add("Karen", 73); var grades = temp;
Tak więc, aby dodać to do swojej własnej klasy, wszystko, co musisz zrobić, jak wspomniano, to zaimplementować
IEnumerable
(ponownie, najlepiejIEnumerable<T>
) i utworzyć jedną lub więcejAdd()
metod: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(); } }
Następnie możesz go używać tak, jak robią to kolekcje BCL:
public class MyProgram { private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 }; // ... }
(Aby uzyskać więcej informacji, zobacz MSDN )
źródło
List<int> temp = new List<int>(); temp.Add(1); ... List<int> a = temp;
Oznacza to, żea
zmienna nie jest zainicjowana, aż po cały dodaje nazywa. W przeciwnym razie legalne byłoby zrobienie czegoś takiego,List<int> a = new List<int>() { a.Count, a.Count, a.Count };
co jest szalone.List<int> a; a = new List<int>() { a.Count };
iList<int> a = new List<int>() { a.Count };
T x = y;
jest to to samo coT x; x = y;
, Ten fakt może prowadzić do dziwnych sytuacji. Na przykładint x = M(out x) + x;
jest całkowicie legalny, ponieważint x; x = M(out x) + x;
jest legalny.IEnumerable<T>
; nieogólnaIEnumerable
wystarczy, aby umożliwić użycie składni inicjatora kolekcji.using (var x = new Something{ 1, 2 })
nie usunie obiektu, jeśli jedno zAdd
wywołań się nie powiedzie.Jest to tak zwany cukier syntaktyczny .
List<T>
jest „prostą” klasą, ale kompilator traktuje ją w specjalny sposób, aby ułatwić Ci życie.Jest to tak zwany inicjator kolekcji . Musisz wdrożyć
IEnumerable<T>
iAdd
metody.źródło
Zgodnie ze specyfikacją C # w wersji 3.0 „Obiekt kolekcji, do którego zastosowano inicjator kolekcji, musi być typu implementującego System.Collections.Generic.ICollection dla dokładnie jednego T.”
Jednak te informacje wydają się być niedokładne w chwili pisania tego tekstu; zobacz wyjaśnienie Erica Lipperta w komentarzach poniżej.
źródło
Działa dzięki inicjatorom kolekcji, które w zasadzie wymagają, aby kolekcja zaimplementowała metodę Add i która wykona pracę za Ciebie.
źródło
Kolejną fajną rzeczą dotyczącą inicjatorów kolekcji jest to, że możesz mieć wiele przeciążeń
Add
metody i możesz je wywoływać w tym samym inicjatorze! Na przykład to działa: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 };
Wywołuje prawidłowe przeciążenia. Ponadto szuka tylko metody o nazwie
Add
, typem zwracanym może być wszystko.źródło
Składnia przypominająca tablicę jest zmieniana w serii
Add()
wywołań.Aby zobaczyć to w dużo bardziej interesującym przykładzie, rozważmy następujący kod, w którym robię dwie interesujące rzeczy, które najpierw brzmią nielegalnie w C #, 1) ustawiając właściwość readonly, 2) ustawiając listę z tablicą taką jak inicjator.
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"} };
Ten kod będzie działał idealnie, chociaż 1) MyList jest tylko do odczytu i 2) ustawiam listę za pomocą inicjatora tablicy.
Powodem, dla którego to działa, jest to, że w kodzie, który jest częścią inicjalizatora obiektów, kompilator zawsze zmienia dowolną
{}
podobną składnię na serięAdd()
wywołań, które są całkowicie legalne nawet w polu tylko do odczytu.źródło