web-dev-qa-db-ja.com

プロキシクラスを動的に作成する

プロキシクラスを動的に作成しようとしています。これを行うための非常に優れたフレームワークがあることは知っていますが、これは純粋に学習プロジェクトとしてのペットプロジェクトなので、自分でやりたいと思います。

たとえば、インターフェイスを実装する次のクラスがある場合:

interface IMyInterface
{
    void MyProcedure();
}

class MyClass : IMyInterface
{
    void MyProcedure()
    {
        Console.WriteLine("Hello World");
    }
}

それらをログに記録するためにこのクラスのメソッドをインターセプトするために、同じインターフェースを実装しているが「実際の」クラスへの参照を含む別のクラス(プロキシクラスの私のバージョン)を作成しています。このクラスはアクション(ロギングなど)を実行してから、実際のクラスで同じメソッドを呼び出します。

例えば:

class ProxyClass : IMyInterface
{
    private IMyInterface RealClass { get; set; }

    void MyProcedure()
    {
        // Log the call
        Console.WriteLine("Logging..");

        // Call the 'real' method
        RealClass.MyProcedure();
    }
}

次に、呼び出し元は代わりにプロキシクラスのすべてのメソッドを呼び出します(実際のクラスの代わりに基本的な自家製のIoCコンテナを使用してプロキシクラスを注入しています)。実行時にRealClassを同じインターフェイスを実装する別のクラスにスワップアウトできるようにするため、このメソッドを使用しています。

実行時にProxyClassを作成し、そのRealClassプロパティを設定して、実際のクラスのプロキシとして使用できるようにする方法はありますか?これを行う簡単な方法はありますか、Reflection.EmitおよびMSILを生成しますか?

36
Dave S

System.Runtime.Remoting.Proxies.RealProxy をご覧ください。これを使用して、呼び出し元の観点からはターゲットタイプのように見えるインスタンスを作成できます。 RealProxy.Invokeは、基になる型でターゲットメソッドを呼び出すか、呼び出しの前後に追加の処理(ログ記録など)を実行できるポイントを提供します。

各メソッド呼び出しの前後にコンソールにログを記録するプロキシの例を次に示します。

public class LoggingProxy<T> : RealProxy
{
    private readonly T _instance;

    private LoggingProxy(T instance)
        : base(typeof(T))
    {
        _instance = instance;
    }

    public static T Create(T instance)
    {
        return (T)new LoggingProxy<T>(instance).GetTransparentProxy();
    }

    public override iMessage Invoke(iMessage msg)
    {
        var methodCall = (IMethodCallMessage)msg;
        var method = (MethodInfo)methodCall.MethodBase;

        try
        {
            Console.WriteLine("Before invoke: " + method.Name);
            var result = method.Invoke(_instance, methodCall.InArgs);
            Console.WriteLine("After invoke: " + method.Name);
            return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception: " + e);
            if (e is TargetInvocationException && e.InnerException != null)
            {
                return new ReturnMessage(e.InnerException, msg as IMethodCallMessage);
            }

            return new ReturnMessage(e, msg as IMethodCallMessage);
        }
    }
}

使用方法は次のとおりです。

IMyInterface intf = LoggingProxy<IMyInterface>.Create(new MyClass());
intf.MyProcedure();

コンソールへの出力は次のようになります。

呼び出す前:MyProcedure
こんにちは世界
呼び出し後:MyProcedure

51
bradmo

この質問 の説明に従って動的オブジェクトを使用できますが、動的に生成され、強く型付けされたオブジェクトの場合は、Reflection.Emit、あなたが疑ったように。 このブログ には、タイプの動的な作成とインスタンス化を示すコード例があります。

Roslyn には動的プロキシの作成を容易にする機能があるので、こちらもご覧ください。

1
Steve Wilkes

たぶん私は質問を誤解しているかもしれません、コンストラクタはどうですか?

class ProxyClass : IMyInterface
{
    public ProxyClass(IMyInterface someInterface)
    {
        RealClass = someInterface;
    }
   // Your other code...
}
0
Jason Hermann