trsing’s diary

勉強、読んだ本、仕事で調べたこととかのメモ。

EFFECTIVE C# 6.0/7.0 読書メモ 項目14

項目14 初期化ロジックの重複を最小化する

コンストラクタの定義方法について

推奨方法

共通処理を行うためのコンストラクタを用意する

コードの重複を避けることができ、コンストラクタ初期化子に対応するオブジェクトコードがより効率的なものとして生成される。

C#コンパイラはコンストラクタ初期化子を特別な文法とみなし、重複した初期化変数を削除したり、重複した親クラスのコンストラクタ呼び出しを削除したりする。

コンストラクタ初期化子では、別のコンストラクタを1回だけ呼び出すことができる。

public MyClass():
     this(0, "")//コンストラクタ初期化子。MyClass(int initialCount, string name)が呼ばれる
{}
public MyClass(int initialCount, string name)
{}

デフォルト引数を使う

コンストラクタの重複コードを最小化できる。

デフォルト引数を使うと引数に対するデフォルト値を指定することができ、複数のコンストラクタを1つのコンストラクタに置き換えることができる。

public MyClass(int initialCount):
    this(iintialCount, string.Empty)
{}
public MyClass(string name):
    this(0, name)
{}
public MyClass(int initialCount, string name)
{}

public MyClass(int initialCount = 0, string name ="")
{}

例えば引数4個ある場合、各引数に対して入力する、しないなどを考えるとオーバーロードの数は24通り。 デフォルト引数を使うと1つですむ。

注意点
  • new制約(where T : new())があるジェネリッククラスやメソッドと連携するなら引数なしのコンストラクタを用意しておく必要がある。
  • 引数のデフォルト値はコンパイル時定数。string.Emptyはstringクラスのstaticプロパティであり、コンパイル時定数ではないため使用できない。

  • デフォルト引数を使用すると、引数の名前とそのデフォルト値がpublicインターフェイスの一部となる(名前付き引数?)。このため、引数の名前を変更した場合、このクラスを使用するすべてのコードが名前の変更に追従しないといけない。将来起こり得る変更に対しては、オーバーロードされたコンストラクタの方が耐性が高い。

非推奨

最初のコンストラクタを記述し、コピペして必要な分だけコンストラクタをオーバーライドする。

論外

共通する処理をprivateへルパメソッドに切り出す

推奨方法とは異なり重複した処理(オブジェクト初期化子用の文、親クラスのコンストラクタ呼び出し)が削除されない。

コンストラクタではないのでreadonlyの初期化ができない。

最後に

型のインスタンスが初期化される際に発生する処理を把握しておくこと。

  1. static変数のメモリストレージが0に初期化される
  2. static変数の初期化子が実行される
  3. 親クラスのstaticコンストラクタが実行される
  4. staticコンストラクタが実行される
  5. インスタンス変数のメモリストレージが0に初期化される
  6. インスタンス変数の初期化子が実行される
  7. 適切な親クラスのコンストラクタが実行される
  8. インスタンスコンストラクタが実行される

・すべての変数が意図した通りの値に初期化され、かつ初期化処理が1回だけ行われるようなコードを作成せよ