2013-03-31 56 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

查看Castle的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(); 

输出到控制台将随后是:

调用之前: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具有示例代码,显示了Type的动态创建和实例化。

我读过Roslyn这个功能,它使动态代理的创建更容易,所以也许可以在那里看看。