C# の匿名型を返したり渡したりしたい
◆ return するものは dynamic 型で new {a = 1, b = 2}
◆ 受け取るところで同じ型で初期化 var a = new {a = 0, b = 0}
◆ dynamic で受けとって代入 a = received_value
◆ 受け取るところで同じ型で初期化 var a = new {a = 0, b = 0}
◆ dynamic で受けとって代入 a = received_value
匿名型
クラスとして作るまでもないようなもので 複数のデータをまとめて受け渡ししたいってことありますよねJavaScript みたいな動的な言語なら
function a(){
return {val1: 100, val2: "abc"}
}
function main(){
var val = a()
console.log(a.val1)
}
return {val1: 100, val2: "abc"}
}
function main(){
var val = a()
console.log(a.val1)
}
という感じで その場でオブジェクト作れます
C# でも匿名型があって
var a = new { val1 = 100, val2 = "abc" };
Console.WriteLine(a.val1);
Console.WriteLine(a.val1);
とできます
型はコンパイラしかしらないので変数宣言の型は var か dynamic か object にしかできません
object にしてしまうと val1 というプロパティはないといわれて扱えません
dynamic では扱えますが 補完機能や静的解析でエラー検出することができなくなります
JavaScript みたいにちょっとコード変えて再実行が一瞬でできるならともかくコンパイル必要な言語だと エラーがあったときの再実行までの時間が長いので静的解析でエラー教えてくれるのは重要です
dynamic だとタイプミスでプロパティやメソッドが存在しませんと言われる可能性がけっこうありますからね
一番いいのが var です
ちゃんと匿名型を理解してくれてるので普通に自分で定義したクラスのインスタンスみたいに補完できますし 匿名型は書き込み禁止なので書き込みしようとするとエラーもだしてくれます
使えるところと使えないところ
メソッドの中の同じスコープだとこんな感じで匿名型を簡単に使っていけますvar a = new List<int> { 1, 2, 3, 4, 1, 2, 1 };
var b = a.Select((e, i) => {
return new {value = e, index = i};
}).Aggregate(new {sum = 0, product = 1}, (acc, e) => {
return new {sum = acc.sum + e.value, product = acc.product * e.value);
});
Console.WriteLine(b.sum);
// 14
Console.WriteLine(b.product);
// 48
var b = a.Select((e, i) => {
return new {value = e, index = i};
}).Aggregate(new {sum = 0, product = 1}, (acc, e) => {
return new {sum = acc.sum + e.value, product = acc.product * e.value);
});
Console.WriteLine(b.sum);
// 14
Console.WriteLine(b.product);
// 48
var が使える範囲内では特に困ることない使いやすさです
でも 別メソッドなど引数・返り値でしかデータを渡せないところでは var が使えません
dynamic 型で渡すと 匿名型ではなく dynamic 型なので補完が効かなくなってしまいます
メソッドやクラスを超えて匿名型を使う
実は メソッドを超えても 匿名型を使うことができるんですpublic Class1()
{
var a = new { total = 0, cate1 = 0, cate2 = 0, cate3 = 0 };
a = this.calc();
var b = calc2(a);
Console.WriteLine(a);
Console.WriteLine(b);
}
public dynamic calc()
{
return new { total = 600, cate1 = 100, cate2 = 200, cate3 = 300 };
}
public bool calc2(dynamic da)
{
var a = new { total = 0, cate1 = 0, cate2 = 0, cate3 = 0 };
a = da;
return (a.cate1 + a.cate2 + a.cate3) == a.total;
}
{
var a = new { total = 0, cate1 = 0, cate2 = 0, cate3 = 0 };
a = this.calc();
var b = calc2(a);
Console.WriteLine(a);
Console.WriteLine(b);
}
public dynamic calc()
{
return new { total = 600, cate1 = 100, cate2 = 200, cate3 = 300 };
}
public bool calc2(dynamic da)
{
var a = new { total = 0, cate1 = 0, cate2 = 0, cate3 = 0 };
a = da;
return (a.cate1 + a.cate2 + a.cate3) == a.total;
}
このように 同じ型(構造)の匿名型を作ってそこに dynamic 型で渡したデータを代入します
今回は total, cate1, cate2, cate3 の 4 つのプロパティがこの並びで すべて int 型です
ここさえ合わせていれば dynamic 型を通して返り値を受け取って代入したり 引数で受け取って代入したりできます
dynamic 型を匿名型の変数に代入するところだけは静的にエラーチェックされないので間違ったところに代入してないかやプロパティの数を変えるときに片方を変え忘れてるとかないかなどの確認は必要です
いったん初期化するのが面倒ですけど 使うところでプロパティ一覧書いてるほうがプロパティに何があるというのがわかって読みやすいメリットもあります
匿名型がすごく多くのプロパティをもったり 匿名型がネストしていて 初期化が大変すぎるというような場合は匿名型じゃなくちゃんとしたクラスにすることを考えたほうがいいと思います
この方法ではクラス内だけではなく 別クラスにも受け渡しできます
ただし internal 型のようで別の dll には渡せませんでした
var a = new { val = 1 };
a.GetType().IsPublic
// false
a.GetType().IsPublic
// false
C# Interactive や csi 中のスクリプトだと true になります
まとめ
同じ構造の匿名型で作った変数には 同じ構造の匿名型を代入できますメソッド間の受け渡しは dynamic 型で行って 匿名型を受け取って使いたいところで同じ構造の匿名型で変数を初期化してそこに代入すれば補完効く状態で使えます
使えるのは同じアセンブリ内限定です
dynamic 使えばこんなことできるんじゃないかな と思いついてやってみてちゃんと動いたときは 感動ものでした