コンストラクタ初期化子
別のコンストラクタを呼び出す
メソッド内のコードが、同名のオーバーロードされたメソッドを呼び出すことは簡単です。そのまま、呼び出したいメソッドのパラメータに合わせて引数を指定するだけです。
public void M() { M(10); } //M() から M(int) の呼び出し public void M(int i) { }
ところが new 演算子でインスタンスが作成されるときのみ実行されるコンストラクタの場合は、メソッドのように明示的に呼び出すことができません。コンストラクタ内の初期化処理が同一で、既定の値で初期化するか、明示的にパラメータで初期値を指定するかの違いだけでオーバーロードする場合、同じ初期化コードが複数のオーバーロードされたコンストラクタに複製されている状態は、コードの保守性から考えて好ましいものではありません。
class A { public int x, y; public A() { x = 0; y = 0; } public A(int ix, int iy) { x = ix; y = iy; } }
上のコードの A クラスのコンストラクタはオーバーロードされ x フィールドと y フィールドの値を定められた既定の値で初期化するか、もしくはパラメータに渡された値で初期化するかを選べます。この程度であれば上のコードでも問題ないように見えますが、オーバーロードされたコンストラクタの数が多い場合、パラメータが違うだけで同じ初期化コードが複数のコンストラクタに分散します。これは、より良いコード品質を目指すならば避けなければなりません。パラメータを受け取らない A() コンストラクタから、パラメータを受け取る A(int, int) コンストラクタを呼び出すことができれば、x フィールドと y フィールドを初期化するコードは 1 ヶ所にまとめられます。
コンストラクタが、オーバーロードされた別のコンストラクタを呼び出すにはコンストラクタ宣言でコンストラクタ初期化子(constructor initializer)を用います。コンストラクタ初期化子は省略可能であり、これまでは省略してきました。
コンストラクタ修飾子 コンストラクタ名(パラメータリスト) コンストラクタ初期化子 { コンストラクタ本体 }
コンストラクタ初期化子は上記のようにパラメータリストの後、コンストラクタ本体の前に記述します。
自分自身のクラス内にある別のコンストラクタを呼び出すには this キーワードを用いて、以下のようなコンストラクタ初期化子を指定します。
: this ( 引数リスト )
引数リストには、呼び出すコンストラクタに渡す引数をカンマ区切りで指定します。自分自身を呼び出すようなコンストラクタ初期化子は書けません。
class A { public int x, y; public A() : this(0, 0) { } public A(int ix, int iy) { x = ix; y = iy; } }
先ほどのコードをコンストラクタ初期化子を用いて書き直したものです。A() コンストラクタから、A(int, int) コンストラクタを呼び出しています。この時、既定の値を ix パラメータと iy パラメータに渡しています。
上の A() コンストラクタを呼び出した場合、先にコンストラクタ初期化子によって A(int, int) コンストラクタが実行され、その後、上記の例では空ですが A() コンストラクタのコードが実行されます。
class MagicalGirl { public MagicalGirl() : this("魔法少女はじめました") { System.Console.WriteLine("Stand by Ready!"); } public MagicalGirl(string copy) { System.Console.WriteLine(copy); } } class Test { public static void Main(string[] args) { MagicalGirl nanoha = new MagicalGirl(); } }
コード1では MagicalGirl クラスのコンストラクタがオーバーロードされており、パラメータを受け取らないコンストラクタから、文字列を受け取るコンストラクタを呼び出しています。Main() メソッドからはパラメータを受け取らないコンストラクタでインスタンスを生成していますが、実行結果のようにコンストラクタ初期化子に指定した引数で MagicalGirl(string) コンストラクタが実行されています。