匿名型

匿名型 Anonymous typesを用いることで、クラスの宣言を行うことなく一時的なオブジェクトを生成することができます。匿名型は、objectクラスから派生する名前を持たないクラスを new演算子で作成することができます。

anonymous-object-creation-expression:
    new   anonymous-object-initializer

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

member-declarator-list:
    member-declarator
    member-declarator-list   ,   member-declarator

member-declarator:
    simple-name
    member-access
    identifier   =   expression

匿名型の作成には、匿名オブジェクト生成式anonymous-object-creation-expressionは、new 演算子の直後に匿名オブジェクト初期化子anonymous-object-initializerを指定します。通常のインスタンス生成式ではnew演算子の直後に型を指定しますが、匿名型にはそれがありません。

new { メンバ宣言リスト }

匿名オブジェクト初期化子の{ }内にはメンバ宣言子リスト member-declarator-listを指定します。メンバ宣言子リストは、メンバ宣言子member-declaratorをカンマ , で区切ったリストです。メンバ宣言子は簡単名simple-name、メンバアクセスmember-access、または識別子と初期値を指定する式のいずれかとなります。

メンバ宣言子は、生成する匿名型オブジェクトのプロパティの識別子と値を決定します。例えば、次の匿名オブジェクト生成式はXとYという名前の整数型のプロパティを持つ匿名オブジェクトをインスタンス化します。

new { X = 10, Y = 100 }

プロパティの型は自動的に推論されるため、nullを初期値に指定することはできません。

匿名型は、コンパイル時にコンパイラによって自動的に名前が与えられますが、ソースから匿名型にアクセスすることはできません。そのため、匿名型のオブジェクトを保存する変数には暗黙に型付けされたローカル変数varを用います。

var pt = new { X = 10, Y = 100 }

この場合、pt変数は匿名型のオブジェクトのメンバにアクセスできます。Visual Stduio 2008であれば、インテリセンスも正しく反応してくれます。varはローカル変数しか使うことができないため、性質上、匿名オブジェクトはローカル変数内に留めるべきでしょう。グローバル変数に匿名オブジェクトを保存して利用したい場合は、object型として保存し、プロパティへのアクセスはリフレクションを利用する必要があります。

サンプル 07

class Test
{
    static void Main(string[] args)
    {
        var pt = new { X = 10, Y = 20 };
        System.Console.WriteLine("X={0}, Y={1}", pt.X, pt.Y);
    }
}

実行結果

X=10, Y=20

匿名オブジェクトのプロパティの識別子、型、並びが同一の場合は、同じクラスのインスタンスとして扱われます。実際にコンパイラによって生成されるクラスも共有されます。

var pt1 = new { X = 10, Y = 20 };
var pt2 = new { X = 10, Y = 20 };
pt1 = pt2;

上記のpt1変数にpt2変数のオブジェクトを代入するコードは正しくコンパイルすることができます。これは、pt1とpt2変数に代入している匿名オブジェクトが、同じクラスから生成されるためです。匿名オブジェクトのプロパティの名前や型、並びが異なっている場合、異なるクラスのオブジェクトと解釈されます。

var pt1 = new { X = 10, Y = 20 };
var pt2 = new { Y = 10, X = 20 };
pt1 = pt2;

上記の場合、匿名オブジェクトのプロパティ名の並びが異なるため、異なる匿名クラスが生成されます。よって、pt1変数とpt2変数の型が異なってしまい、代入はエラーとなります。

メンバ宣言子には、単純な識別子だけを指定する簡単名と、オブジェクトのメンバを参照するメンバアクセスを指定することも可能です。これらを指定した場合、識別子、またはメンバと同じ名前のプロパティが宣言されます。

サンプル08

using System.Drawing;

class Test
{
    static void Main(string[] args)
    {
        int X = 10, Y = 20;
        Size obj = new Size() { Width = 100, Height = 200 };
        var pt1 = new { X, Y };
        var pt2 = new { obj.Width, obj.Height };

        System.Console.WriteLine("X={0}, Y={1}", pt1.X, pt1.Y);
        System.Console.WriteLine(
             "Width={0}, Height={1}", pt2.Width, pt2.Height);
    }
}

実行結果

X=10, Y=20
X=100, Y=200

サンプル08では、整数型の変数XとYからpt1変数に代入する匿名オブジェクトを生成し、Size型の変数objからpt2変数に代入する匿名オブジェクトを生成しています。

pt1変数を初期化している匿名オブジェクト生成式は、次の匿名オブジェクト生成式と同義です。

new { X = X, Y = Y }

pt2変数を初期化している匿名オブジェクト生成式は、次の匿名オブジェクト生成式と同義です。

new { Width = obj.Width, Height = obj.Height }

このように、単純な識別子やメンバアクセスによる値が指定された場合は、識別子やメンバ名がプロパティの名前として利用されます。