オブジェクト初期化子

C#3.0では、オブジェクトのインスタンスを生成すると同時に、プロパティを初期化するオブジェクト初期化子Object initializersが追加されました。これは、型推論と同じように動的言語の影響を強く受けたものでしょう。この機能によって、インスタンス生成後にプロパティを個別に初期化する面倒から解放されることになります。

object-initializer:
    {   member-initializer-listopt   }
    {   member-initializer-list   ,   }

member-initializer-list:
    member-initializer
    member-initializer-list   ,   member-initializer

member-initializer:
    identifier   =   initializer-value

initializer-value:
    expression
    object-or-collection-initializer

オブジェクト初期化子の構文

オブジェクト初期化子は{ }内にオブジェクトのメンバを初期化するメンバ初期化子member-initializerのリストを指定することができます。メンバ初期化子は、オブジェクトが保有するメンバ名に続いて = トークン、その後にメンバ代入する式、または別の初期化子を指定します。

{ メンバ名 = 初期値, ... }

ここで最も重要なことは、初期値initializer-valueに指定できるのは式expressionに加えてobject-or-collection-initializerが許されていることです。object-or-collection-initializerはオブジェクト初期化子と、後述するコレクション初期化子のいずれかであることを表します。つまり、オブジェクト初期化子は入れ子構造にすることができるのです。JSONのような書き方ができると言うと分かりやすいでしょうか。

サンプル05

class User
{
    private int id;
    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;
    public string Name
    {
         get { return name; }
         set { name = value; }
    }

    public override string ToString()
    {
        return "ID=" + id + ", Name=" + name;
    }
}

class Test
{
    static void Main(string[] args)
    {
        User user1 = new User() { ID = 1, Name = "戦人" };
        User user2 = new User() { ID = 2, Name = "朱志香" };
        System.Console.WriteLine(user1);
        System.Console.WriteLine(user2);
    }
}

実行結果

ID=1, Name=戦人
ID=2, Name=朱志香

サンプル05は、自作のUserクラスのオブジェクトをMain()メソッド内でオブジェクト初期子を用いて生成と同時に初期化しています。例えば、user1 変数の初期化は以下のコードと同じ意味を持ちます。

User user1 = new User();
user1.ID = 1;
user1.Name = "戦人";

どちらの生産性が高いかは一目瞭然でしょう。コードとしてもオブジェクト初期化子を用いた方が読みやすく、簡単です。特に、.NET Framework 3.0で追加されたWPFのクラス群はXAMLを対象としているため、コンストラクタでプロパティを初期化するという文化を持ちません。WPFのUIElementを継承したクラスの多くは、インスタンス生成後にプロパティを初期化する必要があります。XAMLを使わずにC#コードでWPFアプリケーションを開発する場合、オブジェクト初期化子は一定の効力を発揮するでしょう。

コレクション初期化子

コレクション初期化子Collection initializersは、配列初期化子と同じ構文でコレクション型を実装するオブジェクトを初期化する方法を提供します。配列と同じような性質をもつコレクション型ですが、要素の初期化はインスタンスの生成後にAdd()メソッドやAddRange()を用いる必要がありました。

コレクション初期化子は、System.Collections.Generic.ICollection<T>型を実装しているインスタンスの初期化子として利用することができます。コレクション初期化子を用いることでList型のようなICollectionインタフェースを実装しているクラスのインスタンス生成時に、配列と同じように要素を初期化することができます。

collection-initializer:
    {   element-initializer-listopt   }
    {   element-initializer-list   ,   }

element-initializer-list:
    element-initializer
    element-initializer-list   ,   element-initializer

element-initializer:
    non-assignment-expression

コレクション初期化子の構文

コレクション初期化子は、{ }の内部に要素初期化子リストelement-initializer-listを指定します。要素初期化子リストは、要素の値を指定する要素初期化子element-initializerをカンマで区切ったリストです。要素初期化子に指定するnon-assignment-expressionとは、代入演算式を除いたラムダ式やクエリ式を含めた式を表します。

代入演算式が含まれている場合、オブジェクト初期化子の構文と衝突してしまうためx = yといった式を要素初期化子に指定することはできません。ただし(x = y)は可能です。

サンプル06

using System.Collections.Generic;

class User
{
    private int id;
    public int ID
    {
        get { return id; }
        set { id = value; }
    }

    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public override string ToString()
    {
        return "ID=" + id + ", Name=" + name;
    }
}

class Test
{
    static void Main(string[] args)
    {
        List<User> users = new List<User> {
            new User() { ID = 1, Name = "戦人" },
            new User() { ID = 2, Name = "朱志香" },
            new User() { ID = 2, Name = "紗音" }
        };
        foreach (var user in users) System.Console.WriteLine(user);
    }
}

サンプル06は、複数の User クラスのオブジェクトを格納する List<User>型のインスタンス生成時に、コレクション初期化子を用いて User オブジェクトを要素に追加しています。加えて、User オブジェクトのインスタンス生成にはオブジェクト初期化子を用いています。

因みに、上記の処理をオブジェクト初期化子やコレクション初期化子を用いずに書いた場合は次のようになります。

List<User> users = new List<User>();

User user1 = new User();
user1.ID = 1;
user1.Name = "戦人";
users.Add(user1);

User user2 = new User();
user2.ID = 2;
user2.Name = "朱志香";
users.Add(user2);

User user3 = new User();
user3.ID = 3;
user3.Name = "紗音";
users.Add(user3);

生成されたアセンブリを覗いてみると、上記のコードに近い結果が得られます。オブジェクト初期化子やコレクション初期化子を使うことで、コードを大幅に短縮できています。