2016-09-01 11 views
12

私はこのコード例を見ており、リストに配列初期化子を割り当てるようです。私はそれが動作しないと思ったが、何とかそれがコンパイルされます。 {}は配列の初期化子ではありませんか?子はIList型です。中括弧の前に "新しいリスト"がなければどうしますか?このリスト割り当てはどのように機能しますか?

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

編集:私はChildren = new List<View>{ nameLabel, twitterLabel }を試したとき、コンパイラはこの警告を与える:「プロパティまたはインデクサLayout.Childrenがに割り当てることができない、それは読み取り専用です。」コレクション初期化子の特殊なケースだhttps://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/

+0

私はちょうど先日もこれを発見しました。私はそれがどのように動作するかを正確に分離することはできませんでした。 – DLeh

+1

[イニシャライザのMSDNドキュメント](https://msdn.microsoft.com/en-us/library/bb384062.aspx)によれば、この初期化子を使用しているときにコンパイルが繰り返し「追加」を呼び出すようです。この構文を変数宣言で直接使用することはできません。例えば'IList children = {" childfoo "、" childbar "}'はコンパイルされません –

+0

@ stephen.vakil:時には、あなたはほとんど可能です。 '' children' {'childfoo"、 "childbar"}; ' – recursive

答えて

10

のコードは、方法によってXamarinからです。

C#では、配列初期化子の中括弧は、コレクションクラスのコンストラクタで動作するように一般化されています。

いずれのクラスも、System.Collections.IEnumerableを実装し、1つ以上のAdd()メソッドを持つクラスをサポートします。 Eric Lippert has a good post about this type of "pattern matching" in C#:コンパイラがここで行っているのは、継承とインターフェイスの実装に基づいてクラスの機能が認識される従来型の厳密に型指定されたOOPではなく、「duck typing」と呼ばれるものです。 C#はいくつかの場所でこれを行います。私が知らなかったその記事にはたくさんのものがあります。

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}"); 
    } 
} 

そして、これはコンパイル:

var f = new Foo(); 

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

あなたはこれらとかなり奇妙なゲームをプレイすることができます。このためにシンタックスシュガーです

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

。私はそれが良い考えであるかどうか分からない(実際にはdoを知っている - それはできません)。私は、コレクションの初期化子でオプションを定義できるコマンドラインパーサークラスを作成しました。 Addの数多くのオーバーロードがあり、さまざまなパラメータリストがあり、その多くは汎用的です。コンパイラが推論できるものはすべて、公正なゲームです。

また、これを減らすことなく、機能の悪用のポイントに戻ることができます。

は何を見ていると、それはあなたがクラス自体が既に作成された非アサインメンバーのコレクション初期化子を行うことができます同じ初期化子構文を拡張したものです:

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

そして今... 。

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

{ 0, "Beringia" }Barが自身のために作成したFooインスタンスのコレクション初期化子です。それは、このために糖衣構文です:あなたはそれをそのように見たときに、構文糖の初期化子の使用にFoo.Add()の過負荷を解決するための

var b = new Bar(); 

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

コンパイラの意欲が理にかなっています。私はそれを行うことができるのはすばらしいことだと思っていますが、彼らが選んだ構文に100%満足しているわけではありません。代入演算子が赤いニシンであることがわかった場合、他のものもそうです。

しかし、私は構文アービターではありません。それはおそらくすべての関係者に最適です。

最後に、これは、オブジェクト初期化子で動作します:

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" }; 
} 

だから... ...実際に変身

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

...

var b = new Bar(); 

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

我々は初期化できませんBar.Bazはセッターがないので、プロパティを初期化できるのと同じようにプロパティを初期化できますems in Foo。実際のコンストラクタにアタッチされた別のオブジェクトイニシャライザによって既に初期化されていても、それは当てはまります。

コレクションイニシャライザは、累積しています:Bar.Fooは、{ "1000", "0", "Beringia" }の3つのアイテムを持ちます。

中括弧は代入文の列の省略形またはAdd()オーバーロード呼び出しのように考えると、すべてがフォーカスに合っています。

しかし、左辺値が実際に割り当てられていない場合には、等号が不安定であることに同意します。ここでボーナス

from that Eric Lippert article私が学んだもう一つのパターンマッチング機能です。そのため

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

...

var x = from s in HoldMyBeerAndWatchThis select s; 

あなたが仕事へselectのために必要なすべてがあるという事ます」から選択するにはSelectという名前のメソッドがあり、のようなものが返されますIEnumerableは、foreachthe linked article(感謝します!Eric!)の@EricLippertの発言に概説されており、Func<T,T>パラメータをとります。

+8

' 'Children''部分はコレクションの初期化子ですが、これはOPが実際に求めているものだと思います。 –

+0

@JonSkeet Ohhhh、duh、そうです。 Frantically –

+0

の更新ドキュメントの参照:http://stackoverflow.com/documentation/c%23/21/collection-initializers/7975/using-collection-initializer-inside-object-initializer#t=201609011535273502169 – DLeh

3

必ずしもそうではありません。あなたが考えているのは:

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

これは配列の初期化です。しかし、あなたも持っている:

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

これはオブジェクトの初期化子です。あなたの "置き換えられた"壊れたコードの中には、コレクションの初期化子というものがあります。これは、IEnumerableを実装し、適切な引数(この場合はpublic void Add(View view)のようなもの)を使用してAddメソッドを実装するすべての型で機能します。

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

あなたのケースでは、後者の2つが使用され、コレクション初期化子は新しいコレクションをインスタンス化しません。単純なサンプルコード:この場合

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>(); 
    } 
} 

Mainのコードはおおよそこの等価C#コードに変換:

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

このフォームは、しかし、オブジェクト初期化子内部でのみ有効である - それは、具体的に設計されていますreadonlyフィールドがある場合は、オブジェクト/コレクション初期化子構文を使用して初期化する必要があります。たとえば、これは動作しません:

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

コンパイラは前と同じ「トリック」を使用した場合、それが唯一のコレクションに項目を追加することができます - にもかかわらず(それは、コレクションが空であるという保証がありませんオブジェクトイニシャライザの場合、これは「慣例による保証」に過ぎません。最初にいくつかの項目を含むリストを初期化すると、「割り当て」が発生したときにそれらが保持されます。

すべてです、人々:)

+0

私が理解する限り、これは、オブジェクトの初期化の中でコレクションの初期化を行う場合にのみ適用される特別な規則です。これに関する公式の文書はありますか? –

+0

@ JohnL。私が知る限り、最後のC#ECMA仕様にはまだオブジェクトイニシャライザがありませんでした。私はこれに関する公式の文書はないと思う。 – Luaan

+1

もちろんこれに関する公式の文書があります。この機能は10年以上前に実装されました*。過去10年間の公表されたC#仕様、つまりMSDN、またはデッドツリー「C#プログラミング言語」を参照してください。 –

5

これは実際にどのように動作するかのように、他の2つの答えではいくつかの混乱があるようです。

等号の後のコレクションイニシャライザを指定するメンバーイニシャライザは、埋め込みコレクションの初期化です。新しいコレクションをフィールドまたはプロパティに割り当てる代わりに、イニシャライザで指定された要素がフィールドまたはプロパティによって参照されるコレクションに追加されます。

お客様の便宜のため、仕様が公開されています。 C#言語構造の意味について質問がある場合は、それ(または注釈付きバージョン「The C#Programming Language」)を最初に参照する必要があります。

関連する問題