DynamicObject を使ってみよう その 2
前回の「DynamicObject を使ってみよう」の続き。
前回は、DynamicObject と ExpandoObject を使ってみた。
DynamicObject の派生クラスや ExpandoObject は連想配列のように機能した。
但し、通常の連想配列とはインタフェイスが異なる。
今回も、引き続き DynamicObject と ExpandoObject を使ってみよう。
■ DynamicObject を使った例
DynamicObject の派生クラスがインタフェイスの異なる連想配列のように使える、と云うことを利用してちょっとしたラッパー クラスを作ってみよう。
例えば、ASP.NET や ASP.NET MVC では、セッション変数を利用する際、以下のように連想配列のように使う。
Session["Id"] = 100;
int? id = Session["Id"] as int?;
DynamicObject を利用したラッパー クラスを作ってみよう。
例えば、こんな感じだ。
using System.Dynamic; using System.Web; public class SessionObject : DynamicObject { readonly HttpSessionStateBase session; public SessionObject(HttpSessionStateBase session) { this.session = session; } // プロパティに値を設定しようとしたときに呼ばれる public override bool TrySetMember(SetMemberBinder binder, object value) { session[binder.Name] = value; return true; } // プロパティから値を取得しようとしたときに呼ばれる public override bool TryGetMember(GetMemberBinder binder, out object result) { result = session[binder.Name]; return true; } }
すると、従来のこんな書き方が、
// ASP.NET MVC の場合の一例 (従来の書き方) using System.Web; using System.Web.Mvc; using System.Web.UI; public class HomeController : Controller { public ActionResult Index() { var id = Session["Id"]; // まだ Session["Id"] は無いので、id は null Session["Id"] = 100; // Session["Id"] に 100 を設定 id = Session["Id"]; // id には 100 が返ってくる var item = Session["Item"]; // まだ Session["Item"] は無いので、item は null Session["Item"] = new { Id = 200, Name = "田中一郎" }; // Session["Item"] に匿名型のオブジェクトを設定 id = DataBinder.Eval(Session["Item"], "Id"); // id には 200 が返ってくる return View(); } }
SessionObject クラスを使うことによって、こんな書き方になる。
// ASP.NET MVC の場合の一例 (SessionObject クラスを使った場合) using System.Web; using System.Web.Mvc; public class HomeController : Controller { dynamic session = null; public ActionResult Index() { session = new SessionObject(Session); var id = session.Id; // まだ session.Id は無いので、id は null session.Id = 100; // session.Id に 100 を設定 id = session.Id; // id には 100 が返ってくる var item = session.Item; // まだ session.Item は無いので、item は null session.Item = new { Id = 200 }; // session.Item に匿名型のオブジェクトを設定 id = session.Item.Id; // id には 200 が返ってくる return View(); } }
dynamic に書けることで、コードがややシンプルになった。
一応、Visual Studio でデバッグ実行し、デバッガーを使って値をチェックしてみると、次のようになった。
まあ、実用的な意味合いは余りないが、連想配列的な部分には、使える可能性がある、と云う一例だ。
■ ExpandoObject を使った例
次は、ExpandoObject だ。
・ExpandoObject にプロパティっぽいものを追加
前回は、ExpandoObject を使って動的なプロパティっぽいものを実現した。
再度やってみよう。
using System; using System.Dynamic; class Program { static void Main() { dynamic item = new ExpandoObject(); // プロパティっぽいものを追加 その1 item.Id = 10; Console.WriteLine(item.Id); } }
実行結果は次の通り。
10
もうちょっと複雑な例。
using System; using System.Dynamic; class Program { static void Main() { dynamic item = new ExpandoObject(); // プロパティっぽいものを追加 その2 item.SubItem = new { Id = 100, Name = "田中一郎" }; Console.WriteLine(item.SubItem); Console.WriteLine(item.SubItem.Id); Console.WriteLine(item.SubItem.Name); } }
実行結果は次の通り。
{ Id = 100, Name = 田中一郎 } 100 田中一郎
・ExpandoObject に static メソッドっぽいものを追加
オブジェクトが何でも追加できるのであれば、デリゲートだって追加できる筈だ。
デリゲートが追加できるのであれば、メソッドっぽいものも実現できるのではないか。
試しに、static メソッドっぽいものの追加を試してみよう。
こんな感じ。
using System; using System.Dynamic; class Program { static void Main() { dynamic item = new ExpandoObject(); // static メソッドっぽいものを追加 item.SetId = new Action<dynamic, int>*1; } }
実行結果は次の通り。
30
・ExpandoObject に イベントっぽいものを追加
デリゲートが追加できるのであれば、もうちょっとイベントっぽくしてみよう。
こんな感じだ。
using System; using System.Dynamic; class Program { static void Main() { dynamic item = new ExpandoObject(); // イベントっぽいものを追加 // 1. 先ず、Update と云う名のイベントっぽいものを用意 item.Update = null; // 2. 次に、Update にイベントハンドラーっぽいものを追加 item.Update += new Action<dynamic, EventArgs>*2; // 3. そして、SetName と云うメソッドっぽいものを用意して、中で Update と云うイベントっぽいものを起こす item.SetName = new Action<dynamic, string>( (o, value) => { o.Name = value; if (o.Update != null) o.Update(o, EventArgs.Empty); } ); // 4. では、試してみよう: // SetName を呼ぶと Update が起きて Console.WriteLine(sender.Name) されるかな? item.SetName(item, "田中次郎"); } }
実行結果は次の通り。
田中次郎
イベントっぽい。
・ExpandoObject のメンバーの一覧
ここまでを纏めてみる。最後にメンバーの一覧を取ってみよう。
using System; using System.Dynamic; class Program { static void Main() { dynamic item = new ExpandoObject(); // プロパティっぽいものを追加 その1 item.Id = 10; Console.WriteLine(item.Id); // プロパティっぽいものを追加 その2 item.SubItem = new { Id = 20, Name = "田中一郎" }; Console.WriteLine(item.SubItem); Console.WriteLine(item.SubItem.Id); Console.WriteLine(item.SubItem.Name); // static メソッドっぽいものを追加 item.SetId = new Action<dynamic, int>*3; // イベントっぽいものを追加 // 1. 先ず、Update と云う名のイベントっぽいものを用意 item.Update = null; // 2. 次に、Update にイベントハンドラーっぽいものを追加 item.Update += new Action<dynamic, EventArgs>*4; // 3. そして、SetName と云うメソッドっぽいものを用意して、中で Update と云うイベントっぽいものを起こす item.SetName = new Action<dynamic, string>( (o, value) => { o.Name = value; if (o.Update != null) o.Update(o, EventArgs.Empty); } ); // 4. では、試してみよう: // SetName を呼ぶと Update が起きて、イベントハンドラーの中で Console.WriteLine(sender.Name) されるかな? item.SetName(item, "田中次郎"); // メンバーの一覧の取得 Console.WriteLine("\nメンバーの一覧:\n"); foreach (var member in item) Console.WriteLine(member); } }
実行結果は次のようになった。
10 { Id = 20, Name = 田中一郎 } 20 田中一郎 30 田中次郎 メンバーの一覧: [Id, 30] [SubItem, { Id = 20, Name = 田中一郎 }] [SetId, System.Action`2[System.Object,System.Int32]] [GetId, System.Func`2[System.Object,System.Int32]] [Update, System.Action`2[System.Object,System.EventArgs]] [SetName, System.Action`2[System.Object,System.String]] [Name, 田中次郎]
追加したプロパティっぽいものや、メソッドっぽいもの、イベントっぽいものが、型が違うだけのオブジェクトとして追加されているのが判る。
因みに、ExpandoObject は、IDictionary<string, Object> を実装しているので、こんな風に一覧を取ることができるし、要素数を取得したり、要素を削除したりすることもできる。
■ 今回のまとめ
今回も、DynamicObject と ExpandoObject を使ってみた。
dynamic な動作は、実行時にオーバーヘッドが大きいので、そこは要注意だが、応用例を考えてみると面白いのではないだろうか。
*1:o, value) => { o.Id = value; }); item.GetId = new Func<dynamic, int>(o => o.Id); // 呼んでみる item.SetId(item, 30); Console.WriteLine(item.GetId(item
*2:sender, _) => Console.WriteLine(sender.Name
*3:o, value) => { o.Id = value; }); item.GetId = new Func<dynamic, int>(o => o.Id); // 呼んでみる item.SetId(item, 30); Console.WriteLine(item.GetId(item
*4:sender, _) => Console.WriteLine(sender.Name