2016-09-01 16 views
12

Widziałem ten przykład kodu i wygląda na to, że przypisuje inicjator tablicy do listy. Myślałem, że to nie zadziała, ale jakoś się kompiluje. Czy {} nie inicjalizator tablicy? Dzieci są typu ILIST. Jak to działa bez "nowej listy" przed nawiasami klamrowymi?Jak działa to przypisanie listy?

 var nameLayout = new StackLayout() 
     { 
      HorizontalOptions = LayoutOptions.StartAndExpand, 
      Orientation = StackOrientation.Vertical, 
      Children = { nameLabel, twitterLabel } 
     }; 

Edycja: Kiedy próbowałem Children = new List<View>{ nameLabel, twitterLabel }, kompilator daje to ostrzeżenie: „własność lub Indexer Layout.Children nie może być przypisany do, to jest tylko do odczytu”.

Kod fragment pochodzi Xamarin przy okazji: https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/

+0

Po prostu odkryłem to również tego samego dnia, nie byłem też w stanie dokładnie określić, jak to działa. – DLeh

+1

Zgodnie z [dokumentami MSDN na temat inicjalizatorów] (https://msdn.microsoft.com/en-us/library/bb384062.aspx) wydaje się, że kompilacja wielokrotnie wywołuje 'Dodaj' podczas używania tego inicjalizatora. Zauważ, że nie możesz użyć tej składni bezpośrednio w deklaracji zmiennej. na przykład 'IList children = {" childfoo "," childbar "}' nie skompiluje –

+0

@ stephen.vakil: Czasami prawie można. 'string [] children = {" childfoo "," childbar "};' – recursive

Odpowiedz

10

to specjalny przypadek inicjator zbiórki.

W języku C# inicjatory nawiasów macierzy zostały uogólnione do pracy z dowolnym konstruktorem klasy kolekcji.

Każda klasa obsługuje te, jeśli implementuje System.Collections.IEnumerable i ma jedną lub więcej metod Add(). Eric Lippert has a good post about this type of "pattern matching" in C#: To, co robi tutaj kompilator, to coś, co nazywają "duck typing", a nie konwencjonalnie silnie typowane OOP, w którym możliwości klasy są rozpoznawane na podstawie dziedziczenia i implementacji interfejsu. C# robi to w kilku miejscach. W tym artykule nie znalazłem zbyt wielu rzeczy.

public class Foo : List<String> 
{ 
    public void Add(int n) 
    { 
     base.Add(n.ToString()); 
    } 
    public void Add(DateTime dt, double x) 
    { 
     base.Add($"{dt.ToShortDateString()} {x}"); 
    } 
} 

A potem to kompiluje:

var f = new Foo { 0, 1, 2, "Zanzibar", { DateTime.Now, 3.7 } }; 

To cukier syntaktyczny na to:

var f = new Foo(); 

f.Add(0); 
f.Add(1); 
f.Add(2) 
f.Add("Zanzibar"); 
f.Add(DateTime.Now, 3.7); 

Można grać całkiem dziwne gry z nich. Nie wiem, czy to dobry pomysł, aby wyjść na całość (w rzeczywistości I do wiem - to nie jest), ale możesz. Napisałem klasę analizatora wiersza poleceń, w której można zdefiniować opcje za pomocą inicjatora kolekcji. Ma kilkanaście przeciążeń Add z różnymi listami parametrów, wiele z nich jest generycznych. Wszystko, co kompilator może wywnioskować, jest grą fair.

Ponownie, możesz zmniejszyć tę liczbę do momentu nadużywania funkcji.

Co widzisz jest rozszerzeniem, gdzie pozwala zrobić inicjator zbiórki na niezbywalnej członka, że ​​sama klasa już utworzonego samej składni inicjatora:

public class Bar 
{ 
    public Foo Foo { get; } = new Foo(); 
} 

A teraz .. .

var b = new Bar { Foo = { 0, "Beringia" } }; 

{ 0, "Beringia" } jest inicjator zbierania na przykład Foo że Bar utworzonej na siebie; to cukier syntaktyczny na to: chęć

var b = new Bar(); 

b.Foo.Add(0); 
b.Foo.Add("Beringia"); 

kompilatora, aby rozwiązać przeciążeniem Foo.Add() w używaniu inicjatora syntaktyczne cukru ma sens, jeśli spojrzeć na to w ten sposób. Myślę, że wspaniale jest móc to zrobić, ale nie jestem w 100% zadowolony z wybranej przeze mnie składni. Jeśli odnalazłeś operatora przypisania jako czerwonego śledzia, inni też.

Ale nie jestem Arbiter składni, a to chyba najlepiej dla wszystkich zainteresowanych.

Wreszcie, również współpracuje z inicjatorów obiektu:

public class Baz 
{ 
    public String Name { get; set; } 
} 

public class Bar 
{ 
    public Foo Foo { get; } = new Foo { 1000 }; 
    public Baz Baz { get; } = new Baz { Name = "Initial name" }; 
} 

Więc ...

var b = new Bar { Foo = { 0, "Beringia" }, Baz = { Name = "Arbitrary" } }; 

które faktycznie zamienia się ...

var b = new Bar(); 

b.Foo.Add(0); 
b.Foo.Add("Beringia"); 
b.Baz.Name = "Arbitrary"; 

Nie można zainicjować Bar.Baz ponieważ nie ma on ustawiacza, ale możemy zainicjować jego właściwości tak, jak możemy to zainicjować ems w Foo. I to prawda, nawet jeśli zostały już zainicjowane przez inicjator innego obiektu dołączony do rzeczywistego konstruktora.

Inicjatory kolekcji, jak można się spodziewać, są kumulatywne: Bar.Foo będą miały trzy pozycje: .

Kiedy myślisz o nawiasach klamrowych jako skrótach do kolumn instrukcji przypisania lub Add() wywołań przeładowania, wszystkie są skupione.

Ale zgadzam się, że znak równości jest drażniący w przypadkach, w których l-wartość nie jest faktycznie przypisana.

Bonus

Oto kolejny wzorzec dopasowania funkcja dowiedziałem się o from that Eric Lippert article:

public static class HoldMyBeerAndWatchThis 
{ 
    public static IEnumerable<int> Select(Func<String, String> f) 
    { 
     yield return f("foo").Length; 
    } 
} 

Dlatego ...

var x = from s in HoldMyBeerAndWatchThis select s; 

Wszystko, czego potrzebujesz do select do pracy jest to, że rzeczą, którą” ponowne wybieranie z musi mieć metodę o nazwie Select, która zwraca coś , który znachoruje jakIEnumerable zgodnie z opisem w komentarzach @ EricLipperta na temat foreach w (dzięki Eric!) I przyjmuje parametr Func<T,T>.

+8

Część "Children" jest inicjatorem kolekcji - myślę, że o to właśnie OP pyta. –

+0

@JonSkeet Ohhhh, duh, racja. Gorączkowo aktualizacja –

+0

Dokumentacja: http://stackoverflow.com/documentation/c%23/21/collection-initializers/7975/using-collection-initializer-inside-object-initializer#t=201609011535273502169 – DLeh

3

Nie zawsze. To, o czym myślisz, to:

int[] array = new int[] { 1, 2, 3, 4 }; 

To inicjator macierzy. Jednak masz również:

SomeObject obj = new SomeObject { Name = "Hi!", Text = "Some text!" }; 

To inicjator obiektu. To, co masz w swoim "zastępczym" złamanym kodzie, to zupełnie co innego - inicjator kolekcji. Działa to z każdym typem implementującym IEnumerable i ma metodę Add z odpowiednimi argumentami (w tym przypadku jest to coś podobnego do public void Add(View view)).

SomeList list = new SomeList { "Hi!", "There!" }; 

W twoim przypadku, te dwa ostatnie są wykorzystywane, a inicjator zbieranie nie instancję nową kolekcję.Prosty przykładowy kod:

void Main() 
{ 
    var some = new SomeObject { List = { "Hi!", "There!" } }; 

    some.List.Dump(); 
} 

public class SomeObject 
{ 
    public List<string> List { get; private set; } 

    public SomeObject() 
    { 
    List = new List<string>(); 
    } 
} 

W tym przypadku kod w Main przekłada się mniej więcej na tym równoważny kod C#:

var some = new SomeObject(); 
some.List.Add("Hi!"); 
some.List.Add("There!"); 

Ta forma jest ważna tylko wewnątrz inicjatora obiektu, chociaż - to specjalnie zaprojektowany w przypadku, gdy masz pole readonly, które musisz zainicjować za pomocą składni inicjatora obiektu/kolekcji. Na przykład, to nie działa:

var some = new SomeObject(); 
some.List = { "Hi!", "There!" }; 

Jeśli kompilator użyty ten sam „sztuczki”, jak wcześniej, to może tylko dodać elementy do kolekcji - ale nie ma gwarancji, że kolekcja jest pusta (choć nawet w przypadku inicjatora obiektu, jest to tylko "gwarancja przez konwencję" - jeśli najpierw zainicjujesz listę z pewnymi pozycjami, pozostaną one, gdy pojawi się "przypisanie").

To wszystko ludzie :)

+0

Tak dalece, jak rozumiem, jest to specjalna reguła, która ma zastosowanie tylko w przypadku inicjalizacji kolekcji wewnątrz inicjalizacji obiektu. Czy jest na to oficjalna dokumentacja? –

+0

@JohnL. O ile mi wiadomo, ostatnia specyfikacja C# ECMA nie miała jeszcze inicjalizatorów obiektów. Nie sądzę, że istnieje jakakolwiek oficjalna dokumentacja na ten temat. – Luaan

+1

Oczywiście istnieje oficjalna dokumentacja na ten temat; ta funkcja została wdrożona * ponad dziesięć lat temu *. Zobacz wszystkie opublikowane specyfikacje C# lub MSDN, lub martwe drzewa "Język programowania C#" z ostatnich dziesięciu lat. –

5

Wydaje się, że pewne zamieszanie w dwóch innych odpowiedzi, jak to faktycznie działa. Skieruję cię do sekcji specyfikacji C# na temat inicjalizatorów obiektów, która wyraźnie podsumowuje semantykę:

Inicjator elementu, który określa inicjator kolekcji po znaku równości, jest inicjacją osadzonej kolekcji. Zamiast przypisywania nowej kolekcji do pola lub właściwości elementy podane w inicjatorze są dodawane do kolekcji przywoływanej przez pole lub właściwość.

Pamiętaj, że specyfikacja została opublikowana dla Twojej wygody; jeśli masz pytanie dotyczące znaczenia konstruktu języka C#, to (lub wydrukowana wersja z komentarzem "Język programowania C#") powinno być twoim pierwszym odnośnikiem.