連載:[完全版]究極の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の値を書き換えるしかない。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter13 自動実装と自動定義
    1.13.1 ラムダ式を使ったダーティテク―refの代役
    2.13.2 自動実装プロパティ
    3.13.3 自動実装プロパティのアクセス制御
    4.13.4 読み出し専用、書き込み専用はない
    5.13.5 “名無し”のクラス―匿名型
    6.13.6 匿名型の等価性
    7.13.7 匿名型の簡易記法
    8.13.8 匿名型の使用目的
    9.13.9 オブジェクト初期化子
  10.13.10 オブジェクト初期化子の本質とは?
    11.13.11 コレクションはreadonlyでも初期化できる
    12.13.12 オブジェクト初期化子の使用例/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間