DynamicProxyとリフレクション

拙作の.NET Framework2.0用DIコンテナ(Seasar2.4クローン)のAOP機能でも採用しており、ここの日記でも何度か紹介しているDynamicProxy(Castle.DynamicProxy)だが、DynamicProxyにより拡張された型から、リフレクションによりメンバ情報を取り出すときには注意が必要である。

以下のような、オブジェクトに対して任意の名前のプロパティ情報を取得するメソッドがあったとしよう。

public PropertyInfo GetPropertyInfo(object target, string propName)
{
    return target.GetType().GetProperty(propName);
}

.NETの一般的な型(クラス)であれば、なんてことの無い処理だがDynamicProxyで拡張した型から生成したインスタンスをメソッドに食わせると、正しい(存在するはずの)プロパティ名使用しても、以下の例外が発生する場合がある。

System.Reflection.AmbiguousMatchException 
Message: あいまいな一致が見つかりました。
Source: mscorlib
   場所 System.RuntimeType.GetPropertyImpl(String name, BindingFlags bindingAttr, Binder binder, Type returnType, Type types, ParameterModifier modifiers)
   場所 System.Type.GetProperty(String name) ...

この例外の原因は、DynamicProxyが型を拡張するときに、元の型のプロパティの中からプロキシを介する対象となるプロパティを再公開するために、同一のプロパティ名を持つプロパティが型情報に再定義されているからだ。
この問題を回避するには、拡張前の型を退避しておき、その型からプロパティ情報を取得するか、又は以下のようにプロパティ情報を取得する際に、継承元をたどらず対象の型で定義、公開されているプロパティだけを対象にする必要がある。

public PropertyInfo GetPropertyInfo(object target, string propName)
{
    PropertyInfo info = target.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
}

.NETで利用できるプロキシにはいくつかのタイプがあるが、DynamicProxyのように型を拡張するタイプはリフレクション情報が変わることを考慮する必要があるだろう。

追記:
以上のことから、以前にこの日記で公開した二つのクラス
Seasar.Framework.Aop.DynamicProxy.DynamicAopProxy.cs
Seasar.Framework.Aop.DynamicProxy.DynamicProxyMethodInvocation.cs
これらは、インターセプタに処理を移す際に拡張する前の型を退避しておく必要があるだろう。従ってDynamicProxyMethodInvocationクラスのコンストラクタの引数を一つ追加し、拡張前の型をtargetTypeとして渡すように修正した。(これはあくまで拡張前の型を退避、利用したい場合の策。元の型拡張前の型を使わないのであれば、修正する必要は無い)