DynamicObjectでラップするべからず

概要

C# の DynamicObject を知っている人であれば、 Hm.NetCOM の hm.Macro.Functionやhm.Macro.Statement 、あるいは、hm.Macro.Var などを
DynamicObjectなどでラップしたくなるかもしれません。

しかし、一般的な傾向としては、Visual Studio の強力な型補完や入力補完を失うだけとなり、
結果としてプラスに働きません。

DynamicObject とは...

DynamicObject型を知らない、直接classを継承して使ったことがない人もいるかと思います。

DynamicObjectとは、動的に挙動を変更できるオブジェクトです。
簡単に説明すると、以下のようなものを継承して上書き定義出来ます。
通常は全て定義するわけではなく、自分で使いたい所だけ定義します。

# メソッド名 何を定義してるか 説明
1 TryBinaryOperation d + d2 2項目の演算
2 TryConvert (Hoge)d インデックスアクセスでの値の取得
3 TryGetIndex d[0] @twitter
4 TryGetMember d.Hoge フィールド値の取得
5 TryInvoke d() 該当オブジェクト自体に( )を使った関数的な評価の仕方に見立てた実行
6 TryInvokeMember d.Hoge(...) メンバー関数を呼び出した時。引数の内容も分析したりそのまま別へと委譲出来る
7 TrySetIndex d[0] = hoge インデックスアクセスでの値の設定
8 TrySetMember d.Hoge = hoge フィールド値への値の設定
9 TryUnaryOperation -dとかd++ 単項で-や++など単項

例えば、TryInvokeMember の振る舞いを上書き定義すると...

  • TryInvokeMemberを定義したTDynamicFunctionクラスをどこかで用意したとする

    MyTestForm.cs
    TDynamicFunction DynamicFunction = new TDynamicFunction(); // おそらくどこか離れた場所に定義
    
    dynamic success = DynamicFunction.moveto(10,3);
    dynamic result = DynamicFunction.sprintf("%04d", 10);
    

    のような記述が「表面的」には可能となります。
    文字列の面(づら)だけ見ると、それは一見していいように感じます。

    しかしながら現実としては、DynamicObjectの特性上、該当部分周辺の
    引数や返り値において、
    Visual Studio の型とオブジェクトに対する入力補完が効かなくなり、
    入力時とコンパイル時の検証が一切失われてしまいます。

    それを少しでも補うため、恐らくは次のようなことをやらざるを得なくなるでしょう。

    MyTestForm.cs
    using IFnResult = Hm.Macro.IFunctionResult; // おそらくどこか離れた場所に定義
    
    IFnResult result = (IFnResult)DynamicFunction.sprintf("%04d", 10);
    
    // DynamicObjectでラップする前は、TDynamicObjectの定義もなく、そもそも
    // var result = Hm.Macro.Function("sprintf", "%04d", 10);
    // だけだったのに...
    

    結局はdynamicにしたことで、表面的な記述量もあまり変化しない上、
    パフォーマンスや引数周辺のバリデーションチェックだけを失っている形となっています。