FieldGetter と FieldSetter を実装する

この実装には、今度こそリフレクションを利用することとなる。

  public override IMessage Invoke(IMessage msg)
  {
    :(前略)
      IMethodCallMessage req = msg as IMethodCallMessage;

      // FieldGetter と FeildSetter に対して独自の実装を提供する
      if ((req.MethodBase.DeclaringType == typeof(object))
        && req.MethodBase.IsSpecialName)
      {
        switch (req.MethodName)
        {
          case "FieldGetter": return this.FieldGetter(req);
          case "FieldSetter": return this.FieldSetter(req);
        }
      }

    :(後略)
  }

まずは、上記の例のように FieldGetter と FieldSetter だけ独自の実装を行うように分岐する必要があり、FieldGetter と FieldSetter を別途作成しよう。
まずは、次のコードを見てほしい。

  private IMethodReturnMessage FieldGetter(IMethodCallMessage req)
  {
    Type t = Type.GetType(req.Args[0] as string);

    FieldInfo fi = t.GetField(
                       req.Args[1] as string,
                       BindingFlags.Instance|BindingFlags.Static|
                       BindingFlags.Public|BindigFlags.NonPublic);

    object[] outArgs = new object[]
    {
      null,
      null,
      Convert.ChangeType(fi.GetValue(this.target), fi.FieldType),
    };

    return new ReturnMessage(
                   typeof(void), outArgs, outArgs.Length,
                   req.LogicalCallContext, req);
  }

リファレンスを読んだだけでこんなコードが出てきたら、誰も苦労しない。
FieldInfo と値の取得までは、おそらく多くの人が簡単に実装できるだろうが、この FieldGetter というメソッドは、取得した値を第二引数の参照型を通して返す必要がある。
ref/out 型の引数を返すためには、上記のように配列を作成して ReturnMessage クラスに設定する。
リファレンスを読む限り、ref/out 型のパラメータのみを指定すればよいように見えるのだが、実際はすべてのパラメータを返す必要があるようだ。
最後に、void 型メソッドの戻り値は typeof(void) である。
ただし、この typeof(void) という記述は C# に固有のものであることに注意してほしい。
この typeof(void) という記述方法を知る方法だが、void 型の実体を作成するために new System.Void() と、コンストラクタを呼び出してみれば、void 型を必要とする引数には typeof(void) を渡すように親切なエラーメッセージが表示される。
FieldSetter に関しては、上記の内容を理解できれば簡単に作成できるだろうから割愛する。

もう1点、FieldInfo.GetValue() だが、static field に対して this.target を渡しても問題なく動作することはリファレンス上に明記されているので安心してほしいが、Convert.ChangeType() の呼び出しが気になる方もおられろうだろう。*1

実際に自分で利用する限りでは、このような実装が必要になったことはないのだが、Web 上のいくつかの実装経験を調べていると、このような変換操作が必要であることがわかる。
また、id:NyaRuRu さんのページ http://www.dwahan.net/nyaruru/devicehook/ でも、似たような問題を解決する必要があったようだ。*2

やっと

実際に動作する MethodLogger の実装まで話を進めることができた。
あとは this と ContextBoundObject の話を軽くして終わりかな?

IMessage と Invoke まわりは、OOP のオブジェクトとメッセージの関係そのものなので、たいして難しいことではないはずなのだけれども…。

*1:この文章を読んでいる人がどれぐらいいるかわからないけど

*2:この問題が、前述の Convert.ChangeType() によって解決できるかどうかは不明。