LoginSignup
4
4

More than 5 years have passed since last update.

Object initializerはインスタンスを返す前にメンバを初期化する

Last updated at Posted at 2016-03-19

Object initializerの挙動が気になったので調べてみたメモ。

次のコードを考える。

this.Foo = new MyObj();
this.Foo.Prop = 1;

このとき、生成された MyObj インスタンスが Foo プロパティにセットされてから、MyObj インスタンスの Prop プロパティがセットされるまでの間に、ほかのスレッドから Foo プロパティ経由で MyObj インスタンスに触ることができる。MyObj.Prop が設定されるまでは MyObj インスタンスに触ってほしくないのだが、諸事情でコンストラクタの引数を増やしたくないという場面があった。もちろん直接プロパティにセットせずに変数に入れても良いのだけれども、なにかスマートな方法はないものかと思い、調べてみた。

結論から先に言うと、オブジェクト初期化子付のコンストラクタ呼び出しをすることで、この目標は達成できる。

this.Foo = new MyObj() { Prop = 1 };

いくつかの記事では「オブジェクト初期化子とコンストラクト後のプロパティセット(以下プロパティセット)は同義である」と記述されているが、以下の記事では生成されるILコードは異なると主張している。

重要な点を以下に引用する(カッコ内は著者注)。

MethodA(プロパティセット) と MethodB(オブジェクト初期化子) は同じ処理   と 思いきや 全然違いました。
Member プロパティの set である set_Member を実行するタイミングが、
 MethodA では ”参照を value に代入した後” になっていますが、
 MethodB では ”参照を value に代入する前” になっています。

この記事ではILを取り上げて挙動の違いを説明しているが、これがドキュメンテッドな挙動なのかどうかが気になったので、仕様書を読むことにした。

C#の言語仕様書はMSDNからダウンロードできる(恐ろしいことにWordドキュメントの形式で配布されている。C#6.0のspecはいつ公開されるのだろうか)。読むべき項目は「7.6.10.2 Object initializers」だ。

以下に当該箇所を抜粋する。

An instance of Point can be created and initialized as follows:
Pointインスタンスは次のように生成・初期化できる:

Point a = new Point { X = 0, Y = 1 };

which has the same effect as
これは以下のコードと同じ効果を持つ

Point __a = new Point();
__a.X = 0;
__a.Y = 1; 
Point a = __a;

where __a is an otherwise invisible and inaccessible temporary variable.
ここで __a は不可視でアクセスできない一時的な変数である。

つまり、Object initializer内に記述した初期化が行われたあとでオブジェクトへの参照が返ることが仕様書にも明記されている。

結論

インスタンス生成とメンバ初期化をアトミックに行いたいときはObject initializerを使う。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4