2013-03-31 7 views
34

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

例えば、私はインターフェイスを実装する次のクラスを持ち、場合:

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を生成する必要がありますか?

+0

動的に生成するとはどういう意味ですか? Typeオブジェクトがあり、このタイプのオブジェクトをインスタンス化したいということですか? – JJ15k

+0

はい。私は実行時にIMyInterfaceからProxyClassのインスタンスを作成し、その 'RealClass'プロパティに実クラスへの参照を移入して、実クラスへのすべての呼び出しを傍受するために使用できるようにしたいと考えています。質問を編集していただきありがとうございます。 –

+1

城のDynamicProxyを参照してくださいhttp://www.castleproject.org/projects/dynamicproxy/ –

答えて

44

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(); 

出力コンソールになり、その後be:

実行前:MyProcedure
Hello World
invo後ke:MyProcedure

+0

これは私が探していたものであり、正しい方向に私を指摘しています。ありがとうございました。 –

+18

しかし、重要な制限は、プロキシにラップする型がMarshalByRefObjectから派生する必要があることです。 – Piedone

+0

@Piedone、ラップする型はインターフェイスであるか、MarshalByRefObjectから派生します。 – chapluck

0

多分私は疑問を誤解しています、コンストラクタについてはどうですか?

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

私はこのことをお勧めしません。通常、CastleやEntLibなどのよく知られたライブラリを使用します。複雑なクラスの中には、プロキシを動的に生成するのがかなり難しいものがあります。ここでは、 "Is"多型を使ってそれを行う例を示します。このためには、すべてのメソッドを仮想として宣言しなければなりません。あなたがこれをやろうとしていたやり方(「あります」)も可能ですが、私にとってはもっと複雑に見えます。

public class A 
{ 
    public virtual void B() 
    { 
     Console.WriteLine("Original method was called."); 
    } 
} 

class Program 
{ 

    static void Main(string[] args) 
    { 
     // Create simple assembly to hold our proxy 
     AssemblyName assemblyName = new AssemblyName(); 
     assemblyName.Name = "DynamicORMapper"; 
     AppDomain thisDomain = Thread.GetDomain(); 
     var asmBuilder = thisDomain.DefineDynamicAssembly(assemblyName, 
        AssemblyBuilderAccess.Run); 

     var modBuilder = asmBuilder.DefineDynamicModule(
        asmBuilder.GetName().Name, false); 

     // Create a proxy type 
     TypeBuilder typeBuilder = modBuilder.DefineType("ProxyA", 
      TypeAttributes.Public | 
      TypeAttributes.Class | 
      TypeAttributes.AutoClass | 
      TypeAttributes.AnsiClass | 
      TypeAttributes.BeforeFieldInit | 
      TypeAttributes.AutoLayout, 
      typeof(A)); 
     MethodBuilder methodBuilder = typeBuilder.DefineMethod("B", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.ReuseSlot); 
     typeBuilder.DefineMethodOverride(methodBuilder, typeof(A).GetMethod("B")); 


     // Generate a Console.Writeline() and base.B() calls. 
     ILGenerator ilGenerator = methodBuilder.GetILGenerator(); 
     ilGenerator.Emit(OpCodes.Ldarg_0); 
     ilGenerator.EmitWriteLine("We caught an invoke! B method was called."); 

     ilGenerator.EmitCall(OpCodes.Call, typeBuilder.BaseType.GetMethod("B"), new Type[0]); 
     ilGenerator.Emit(OpCodes.Ret); 

     //Create a type and casting it to A. 
     Type type = typeBuilder.CreateType(); 
     A a = (A) Activator.CreateInstance(type); 

     // Test it 
     a.B(); 
     Console.ReadLine(); 
    } 
} 
1

in this questionを説明するように、動的なオブジェクトを使用できますが、動的に生成された、強く型付けされたオブジェクトのためにあなたが疑われるとして、Reflection.Emitを使用する必要があります。 This blogには、タイプの動的作成とインスタンス化を示すサンプルコードがあります。

私はRoslynはので、多分あまりにも、そこを見て、動的プロキシの作成が容易にする機能を持っていることを読みました。

関連する問題