|
|
連載:C# 4入門
第8回 ExpandoObjectを継承した動的拡張できるクラス
株式会社ピーデー 川俣 晶
2011/02/10 |
|
|
値の列挙
実行時に動的にいくらでもメンバを追加できるというと、「いま何が含まれているのか」が気になるところだ。
ExpandoObjectに含まれる内容の一覧は簡単に取得できる。
using System;
using System.Collections.Generic;
using System.Dynamic;
class Program
{
static void Main(string[] args)
{
dynamic x = new ExpandoObject();
x.A = 1;
x.B = 2.0m;
x.C = 3.0d;
x.D = "ExpandoObject Sample";
foreach (KeyValuePair<String, Object> a in x)
{
Console.WriteLine("{0}={1} ({2})",
a.Key, a.Value, a.Value.GetType().Name);
}
}
}
|
|
リスト3 |
A=1 (Int32)
B=2.0 (Decimal)
C=3 (Double)
D=ExpandoObject Sample (String)
|
|
リスト3の実行結果 |
ポイントはforeach文で、個々の値をKeyValuePair<String, Object>型で受けることだ。これに、キーと値のペアが入るので、名前とその値を調べることができる。もちろん、値の型を調べることも普通にできる。
ちなみに、このようなコードが可能であるのは、ExpandoObjectクラスがIEnumerable<KeyValuePair<string, Object>>インターフェイスを実装しているからである。
値のチェック
すでに値が入っているときだけ読み出したい、と思うなら列挙は過剰に強力すぎる。具体的に特定のメンバの有無だけが分かればよい。その方法は用意されている。
using System;
using System.Collections.Generic;
using System.Dynamic;
class Program
{
private static void showAplha(dynamic x)
{
if (((IDictionary<String, Object>)x).ContainsKey("Aplha"))
Console.WriteLine("Alpha={0}", x.Aplha);
else
Console.WriteLine("No Member Alpha here.");
}
static void Main(string[] args)
{
dynamic x = new ExpandoObject();
showAplha(x);
x.Aplha = "Omega";
showAplha(x);
}
}
|
|
リスト4 |
No Member Alpha here.
Alpha=Omega
|
|
リスト4の実行結果 |
ここでのポイントは、ExpandoObjectはIDictionary<String, Object>インターフェイスを実装しているという点である。しかしキャストしないと、このインターフェイスのメソッドにアクセスできない。そのため、このコードではif文の中でキャストを行っている。これで、ContainsKeyメソッドを利用できるようになる。
LINQでクエリする
「列挙できるものはクエリできるもの」という原則からすると、列挙できると分かればLINQでクエリできそうに思える。しかし、これは一筋縄ではいかない。なぜなら、ExpandoObjectは通常dynamic型を経由してアクセスするが、dynamic型はクエリできないからだ。
using System;
using System.Linq;
using System.Dynamic;
class Program
{
static void Main(string[] args)
{
dynamic x = new ExpandoObject();
x.Aplha = 1;
x.Beta = 2;
x.Gamma = 3;
foreach (var v
in ((ExpandoObject)x).Where((n) => (int)n.Value >= 2))
{
Console.WriteLine("{0}={1} ({2})",
v.Key, v.Value, v.Value.GetType().Name);
}
}
}
|
|
リスト5 |
Beta=2 (Int32)
Gamma=3 (Int32)
|
|
リスト5の実行結果 |
解決策は簡単である。変数xをdynamic型から本来のExpandoObject型へキャストして戻すだけである。
あるいは以下のような書き換えでもよい。
foreach (var v in
((IEnumerable<KeyValuePair<String, Object>>)x).Where((n)
=> (int)n.Value >= 2))
|
|
しかし、以下の書き換えはコンパイルできるが、実行はできない。例外が起きる。
foreach (var v in x.Where(
(Func<KeyValuePair<String, Object>,bool>)((n)
=> (int)n.Value >= 2)))
|
|
ExpandoObjectは、IEnumerable<KeyValuePair<String, Object>>インターフェイスを実装しているのでWhereメソッドが使用できるが、ExpandoObject自身は直接これを外部に提供していないからである。IEnumerable<KeyValuePair<String, Object>>型を経由したときだけ利用できる。
メンバの削除
もういらない値が永遠に残り続けるのも無駄である。しかし、名前を指定して代入するとメンバが自動的に生成される機能性だけでは削除できない。この場合は明示的に削除できる。
using System;
using System.Collections.Generic;
using System.Dynamic;
class Program
{
private static void showAplha(dynamic x)
{
if (((IDictionary<String, Object>)x).ContainsKey("Aplha"))
Console.WriteLine("Alpha={0}", x.Aplha);
else
Console.WriteLine("No Member Alpha here.");
}
static void Main(string[] args)
{
dynamic x = new ExpandoObject();
x.Aplha = "Omega";
showAplha(x);
((IDictionary<String, Object>)x).Remove("Aplha");
showAplha(x);
}
}
|
|
リスト6 |
Alpha=Omega
No Member Alpha here.
|
|
リスト6の実行結果 |
削除の機能性はIDictionary<String, Object>インターフェイスにあるので、やはりキャストしてRemoveメソッドを呼び出すだけである。
Insider.NET 記事ランキング
本日
月間