[C#][dynamic] DynamicObject を使ってみよう その 2

Dynamic

前回の「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 でデバッグ実行し、デバッガーを使って値をチェックしてみると、次のようになった。

DynamicObjectを使った例 - デバッガーでの値のチェック - 「まだ session.Id は無いので、id は null」
1. 「まだ session.Id は無いので、id は null」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「id には 100 が返ってくる」
2. 「id には 100 が返ってくる」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「まだ session.Item は無いので、item は null」
3. 「まだ session.Item は無いので、item は null」
DynamicObjectを使った例 - デバッガーでの値のチェック - 「id には 200 が返ってくる」
4. 「id には 200 が返ってくる」

まあ、実用的な意味合いは余りないが、連想配列的な部分には、使える可能性がある、と云う一例だ。

■ 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>((o, value) => { o.Id = value; });
item.GetId = new Func<dynamic, int>(o => o.Id);
// 呼んでみる
item.SetId(item, 30);
Console.WriteLine(item.GetId(item));
}
}

実行結果は次の通り。

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>((sender, _) => Console.WriteLine(sender.Name));
// 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>((o, value) => { o.Id = value; });
item.GetId = new Func<dynamic, int>(o => o.Id);
// 呼んでみる
item.SetId(item, 30);
Console.WriteLine(item.GetId(item));
// イベントっぽいものを追加
// 1. 先ず、Update と云う名のイベントっぽいものを用意
item.Update = null;
// 2. 次に、Update にイベントハンドラーっぽいものを追加
item.Update += new Action<dynamic, EventArgs>((sender, _) => Console.WriteLine(sender.Name));
// 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 な動作は、実行時にオーバーヘッドが大きいので、そこは要注意だが、応用例を考えてみると面白いのではないだろうか。

.NETC#, dynamic

Posted by Fujiwo