はじめに
匿名型、みなさんは使いますか?
var anonymousTypeExample = new {
Name = "Ryota",
Age = 28
};
私は結構使います。主にLINQやRxでの中間データとして使う事が多いです。こんな感じで。
List<string> names = LoadNameList ();
var indexesNames = names.Selct ((string name, int index) => new {name, index});
そんな匿名型ですが、匿名型の入れ子ができたり、配列にできるみたいです。
そもそも匿名型って?
匿名型について以前まとめたのでよろしかったら、こちら
を見てください。
また、MSDNの匿名型もどうぞ。
匿名型は配列にできる
匿名型の配列を作る事ができます。
var persons = new []{
new {
Name = "Taro",
Age = 30,
},
new {
Name = "Jiro",
Age = 27,
},
new {
Name = "Saburo",
Age = 24,
},
};
// foreachでループをまわせる
foreach (var person in persons) {
Console.WriteLine (string.Format("Name:{0} Age:{1}", person.Name, person.Age));
// 匿名型はToStringをオーバーライドしているので、これでもいい感じに表示はされる
// Console.WriteLine (person);
}
var personStringEnumerable = persons.Select (person => person.ToString());
匿名型の配列とobject型の配列
さて匿名型は型がないわけではありません。内部的にはコンパイラが型(クラス)を作っています。(実は内部的にはその型に名前もついています。)
匿名型はクラスですから、object(System.Object)クラスを継承しています。そのため次のようにobject型の変数に匿名型のインスタンスを代入できます。
object anObject = new {
Name = "Ryota",
Age = 28
};
// 下記はコンパイルエラー
// Console.WriteLine (anObject.Name);
// Console.WriteLine (anObject.Age);
ただしコード中のコメントの通りプロパティにアクセスすることはできません。object型に、NameプロパティもAgeプロパティもありませんからね。
同様に匿名型の配列のコードをobjectの配列に変えた場合も、NameやAgeプロパティにアクセスすることはできません。
var objects = new object[]{
new {
Name = "Taro",
Age = 30,
},
new {
Name = "Jiro",
Age = 27,
},
new {
Name = "Saburo",
Age = 24,
},
};
foreach (object anObject in objects) {
// 下記はコンパイルエラー
// Console.WriteLine (string.Format("Name:{0} Age:{1}", anObject.Name, anObject.Age));
}
匿名型の配列と暗黙的に型指定される配列
匿名型以外の配列の生成の初期化+生成を見てみます。ここでは、int型(System.Int32型)を使ってみました。いろいろな書き方が可能ですね。
int[] intArray0 = new int[4]{0, 1, 2, 3};
int[] intArray1 = new int[]{0, 1, 2, 3};
var intArray2 = new int[4]{0, 1, 2, 3};
var intArray3 = new int[]{0, 1, 2, 3};
int[] intArray4 = {0, 1, 2, 3};
//var intArray5 = {0, 1, 2, 3}; これはコンパイルエラーになる
int[] intArray6 = new []{0, 1, 2, 3};
var intArray7 = new []{0, 1, 2, 3};
匿名型の配列を生成する際の記述方法は、最後の記述方法ですね。
var intArray7 = new []{0, 1, 2, 3};
これ以外の記述方法は使えません。これ以外すべての記述方法で型の名前を記述しているからです。匿名型はプログラマが型の名前を用いてコーディングすることはできません。
ここでのミソは、暗黙的に型指定される配列です。配列初期化子に指定された要素から配列の型が推論されて、暗黙的に型指定される配列を作成することができます。
このように、new []{ 匿名型の要素0, 匿名型の要素1 }
という記述で、匿名型の配列を生成する事が可能です。
匿名型の配列っぽいけれど、それコンパイルエラー!
次のコードはコンパイルエラーになります。
// コンパイルエラー
var persons = new []{
new {
Name = "Taro",
Age = 30,
},
new {
Name = "Jiro",
Age = 27,
},
new {
Age = 24,
Name = "Saburo",
},
};
次のコードもコンパイルエラーになります。
// コンパイルエラー
var persons = new []{
new {
Name = "Taro",
Age = 30,
},
new {
Name = "Jiro",
Age = 27,
},
new {
Name = "Saburo",
},
};
なぜか。それは二つとも配列の第三要素の型が違うからです。一つ目の例では第三要素だけプロパティの順序が違いますね。二つ目の例では第三要素だけAgeプロパティがありませんね。
var personA = new {
Name = "Taro",
Age = 30,
}
var personB = new {
Name = "Jiro",
Age = 27,
}
var personC = new {
Age = 24,
Name = "Saburo",
};
var personD = new {
Name = "Shiro",
};
上記のコードでは、personAとpersonBは同じ型です。しかしpersonAとpersonCは違う型です。同様に、personAとpersonDは異なる型です。
ここらへん、ここにまとめてました。
匿名型は入れ子にできる
匿名型は入れ子にもできます。
var anonymousTypeExample = new {
Name = new {
FirstName = "Ryota",
LastName = "Murohoshi"
},
Age = 28
};
匿名型のインスタンスanonymousTypeExampleは、NameプロパティとAgeプロパティを持っています。Ageはint型で、Nameは匿名型です。Nameの匿名型はプロパティとしてString型のFirstNameと同じくstring型のLastNameを持っています。
いつ使う?
匿名型のインスタンスは、引数として渡したり返り値として返す事ができません。そのためメソッド内での利用に使えるシーンが限られています。
匿名型の配列や匿名型の入れ子の利用例・活用例をあげるとしたら、以下に示すようなT4で使う事が考えられます。
var definedClasses = new [] {
new {
ClassName = "Person",
Propeties = new [] {
new { Type = typeof(string), Name = "FirstName" },
new { Type = typeof(string), Name = "LastName" },
new { Type = typeof(int), Name = "Age" },
new { Type = typeof(int), Name = "Sex" },
}
},
new {
ClassName = "Animal",
Propeties = new [] {
new { Type = typeof(string), Name = "Name" },
new { Type = typeof(int), Name = "Age" },
}
},
new {
ClassName = "PlayLog",
Propeties = new [] {
new { Type = typeof(int), Name = "StageId" },
new { Type = typeof(int), Name = "Score" },
new { Type = typeof(DateTime), Name = "PlayedAt" },
}
},
};
上記の匿名型の配列の一つ一つの要素から、T4を用いて、ある形式にそったクラスを静的に生成することを想定しています。
もちろん上記のコードではクラスを定義してもいいです。(上記の場合ClassDefinitionクラスとPropetyDefinitionクラスの二つ)ですが、T4において上記のようなデータを定義するのであれば私は匿名型を使う方が好みです。
上記のように匿名型を用いてそこまで複雑でない構造的なデータを定義すれば、クラス名を記述する必要はなくなり、プロパティ名とそのデータを記述するのみでよくなります。これにより、どのようなデータなのか、そしてその構造がより見やすくなると思います。