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