|
|
連載:[完全版]究極のC#プログラミング
Chapter13 自動実装と自動定義
川俣 晶
2010/02/17 |
 |
|
13.10 オブジェクト初期化子の本質とは?
オブジェクトのフィールド/プロパティを初期化する手段をまとめると、おおむね次の3つに分けられる。
- フィールドの宣言時に初期値を指定する
- コンストラクタで書き込む
- オブジェクト初期化子を使う
これらの性質の違いを把握することは、オブジェクト初期化子を使いこなすうえで重要なポイントになる。
まず、初期化される順番だが、上記の順に実行される。オブジェクト初期化子は最後に実行されるので、クラス内でどのような初期化を行っていても、最終的にそれらは上書きされ、オブジェクト初期化子で指定された値で確定する。
次のリスト13.22を実行すると、そのことが確認できるだろう。
using System;
class A
{
public int Target = 1; // フィールドの宣言時に初期値を指定する
public A()
{
Target = 2; // コンストラクタで書き込む
}
}
class Program
{
static void Main(string[] args)
{
A a = new A() { Target = 3 }; // オブジェクト初期化子を使う
Console.WriteLine(a.Target); // 出力:3
}
}
|
|
リスト13.22 初期化の順番を調べる |
これらの手段間の相違はほかにもある。実は、オブジェクト初期化子は、privateなどのフィールドやreadonlyのフィールドなどは初期化できない(リスト13.23参照)。
using System;
class A
{
private int Target1 = 1;
public readonly int Target2 = 1;
public A()
{
Target1 = 2;
Target2 = 2;
}
}
class Program
{
static void Main(string[] args)
{
A a = new A()
{
Target1 = 3,
// エラー 1 'A.Target1' はアクセスできない保護レベルに
// なっています。
Target2 = 3,
// エラー 2 読み取り専用フィールドに割り当てることは
// できません(コンストラクタ、変数初期化子では可)。
};
Console.WriteLine(a.Target1);
// エラー 3 'A.Target1' はアクセスできない保護レベルに
// なっています。
Console.WriteLine(a.Target2);
}
}
|
|
リスト13.23 初期化できないケース |
なぜ、“フィールドの宣言時に初期値を指定する”、あるいは“コンストラクタで書き込む”のと違って、“オブジェクト初期化子を使う”ことでprivateやreadonlyのフィールドを初期化できないのだろうか?
その理由は、オブジェクト初期化子が、その名に反して「オブジェクト自身の初期化段階」の中で実行されないことにある。オブジェクト初期化子とは、オブジェクト外部に存在するものであり、オブジェクトの初期化が完了するまではオブジェクトに手出しができないのである。そして、外部からオブジェクトに手出しができる段階に入ると、もはやreadonlyのフィールドを書き換えることは許されない。また、あくまで外部から書き込みを行う立場であるから、privateなフィールドに手出しできないのも当然の成り行きといえる。
しかし、これは悪いことばかりではない。オブジェクト初期化子がオブジェクトの外部に存在するということは、オブジェクト初期化子が存在する場所から利用できる資源はすべて利用できるからだ。
たとえば、次のリスト13.24は、オブジェクト初期化子を記述したメソッド(Mainメソッド)の引数を利用して初期化を行っている。
using System;
class A
{
public int Target;
}
class Program
{
static void Main(string[] args)
{
A a = new A() { Target = args.Length };
Console.WriteLine(a.Target);
}
}
|
|
リスト13.24 実行ファイルのパラメータの数で初期化する |
オブジェクト初期化子なしで同様の値によりフィールドTargetを初期化するには、コンストラクタ経由で値を送り込むか、あるいはオブジェクト生成後に明示的にTargetの値を書き換えるしかない。
Insider.NET 記事ランキング
本日
月間