しかし、たとえホリバーや曖昧な用語に目を向けなくても、小さな例を見て、それが問題につながるかどうかを考えましょう。
// position - long position = -1; using (var file = new FileStream("d:\\1.txt", FileMode.Append) { // , // ! Position = position }) { // - }
このフラグメントでは、 usingディレクティブ内でリソース(ファイル)が作成され、そのプロパティの1つ( Position )がオブジェクト初期化子を使用して設定されます。 このコードで最も重要なことは、このプロパティのセッターが例外をスローできることです。
.NETのC ++言語とは異なり、例外の安全性に関する問題に遭遇することはほとんどありませんが、これは例外の観点からコードが安全でないまれなケースの1つです。
これを理解するために、オブジェクト初期化子がコンパイラによってどのように実装されるかを見てみましょう。
class Person { public string Name { get; set; } public int Age { get; set; } } // ... var person = new Person {Name = "John", Age = 42};
一見すると、オブジェクトの初期化子は、その後のプロパティの変更を伴うコンストラクターの呼び出しに過ぎないように思えるかもしれません。 そして、実際には、少し明確になっているだけです。
var tmp = new Person(); tmp.Name = "Jonh"; tmp.Age = 42; var person = tmp;
一時変数は、初期化の「アトミック性」に必要な条件であり、サードパーティコード(別のスレッドなどから)が中間状態のオブジェクトへの参照を取得できません。 さらに、プロパティのセッターから例外をスローすると変数の部分的な初期化が発生するため、一時変数がないと、例外の観点からコードの安全性がさらに低下します。
var tmp = new Person(); tmp.Name = "John"; tmp.Age = 42; var person = tmp;
この場合、いずれかのプロパティのセッターが例外でドロップすると、 _ personフィールドは既に初期化されていますが、最後までではなく、コンストラクタの使用に精通しているオブジェクト初期化子の「原子性」に違反します。
ただし、一時変数は多くの問題を解決しますが、 usingディレクティブ内でオブジェクト初期化子を使用する場合、これは発生しません。 ご存じのように、 usingディレクティブは次のコードに展開されます。
var file = new FileStream("d:\\1.txt", FileMode.OpenOrCreate); try {} finally { if (file != null) ((IDisposable)file).Dispose(); }
ここで、2と2を追加すると、元の例は次のように展開されます。
long position = -1; var tmpFile = new FileStream("d:\\1.txt", FileMode.OpenOrCreate); // ! , Dispose ! tmpFile.Position = position; var file = tmpFile; try { } finally { if (file != null) ((IDisposable)file).Dispose(); }
これは、オブジェクト初期化子を使用して初期化されたプロパティが例外で失敗した場合、オブジェクトのDisposeメソッドが呼び出されないことを意味します。
おわりに
オブジェクト初期化子は便利な機能ですが、使用する
C#言語は、ほとんどの場合かなり予測可能な動作で区別されます(ただし、 可変で意味のあるタイプの微妙な例外などがあります )が、オブジェクトの初期化子をブロックを使用して混合することは直感的ではなく、この組み合わせがどのように設計されているかを理解することをお勧めします不注意。