0

新しいアプリケーションの 1 つに取り組んでいたとき、ばかげた間違いを犯しました… オブジェクト初期化子内に「new ClassName」と書くのを忘れていました。不思議なことに、VS はコードをコンパイルしただけで、エラーや警告はありませんでした。(VS 2015、.Net 4.5.2を使用しました)

私のコード:

var target = new MyClass
{
    MyValues = new List<MyOtherClass>
    {
        new MyOtherClass
        {
            Start = new DateTimeValue
            {
                DateTime = new DateTime(2015, 08, 23)
            },
            End = {
                DateTime = new DateTime(2015, 08, 23)
            }
        }
    }
};

(Start と End はどちらも DateTimeValue 型です)

アプリケーションを起動すると、このコードが NullReferenceException をスローしました。(「新しいDateTimeValue」を追加すると問題が修正されました)。コンパイラからエラーが発生しなかったのはなぜですか?

4

2 に答える 2

9

そこには何も問題はありません。に新しい値をEnd割り当てるのではなく、既存の値を使用してプロパティを設定するオブジェクト初期化子を使用しています。あなたのコードは以下と同等です:End

var tmp0 = new MyClass();
var tmp1 = new List<MyOtherClass>();
var tmp2 = new MyOtherClass();
var tmp3 = new DateTimeValue();
tmp3.DateTime = new DateTime(2015, 8, 23);
// Note the assignment to Start here
tmp2.Start = tmp3;

// No assignment to End -
// it's calling the End "getter" and then the DateTime "setter" on the result
tmp2.End.DateTime = new DateTime(2015, 8, 23);
tmp1.Add(tmp2);
tmp0.MyValues = tmp1;
var target = tmp0;

C# 5 仕様のセクション 7.6.10.2 から:

等号の後にオブジェクト初期化子を指定するメンバー初期化子は、ネストされたオブジェクト初期化子、つまり埋め込みオブジェクトの初期化です。フィールドまたはプロパティに新しい値を割り当てる代わりに、ネストされたオブジェクト初期化子での割り当ては、フィールドまたはプロパティのメンバーへの割り当てとして扱われます。

それがまさにあなたがここでやっていることです - その{ DateTime = ... }部分はオブジェクト初期化子です。

于 2015-08-26T14:52:17.830 に答える
4

それは有効な構文だからです。

大まかに訳すと次のようになります。

var target = new MyClass();
target.MyValues = new List<MyOtherClass>();

var i = new MyOtherClass();

i.Start = new DateTimeValue();
i.Start.DateTime = new DateTime(2015, 08, 23);

i.End.DateTime = new DateTime(2015, 08, 23);

target.MyValues.Add(i);

ただし、プロパティは一度だけ設定され、コンパイラによって読み取られることはありません。追加の一時変数を使用する必要があります (ジョンが彼の答えで行ったように) が、簡単にするためにここにそれらを書くことはしませんでした。

この構文は、コンストラクターでプロパティをMyOtherClassインスタンス化する場合に役立ちます。Endプロパティが読み取り専用の場合にも使用できます。

于 2015-08-26T14:50:44.657 に答える