【问题标题】:Dynamically creating a proxy class动态创建代理类
【发布时间】:2013-03-21 23:08:18
【问题描述】:

我正在尝试动态创建代理类。我知道有一些非常好的框架可以做到这一点,但这纯粹是一个作为学习练习的宠物项目,所以我想自己做。

例如,如果我有以下类实现一个接口:

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?

【问题讨论】:

  • 动态生成是什么意思?你的意思是你有一个 Type 对象并且想要实例化这个类型的对象吗?
  • 是的。我想在运行时从 IMyInterface 创建一个 ProxyClass 的实例,用对真实类的引用填充它的“RealClass”属性,这样它就可以用来拦截对真实类的所有调用。我将编辑问题以澄清谢谢。
  • 查看 Castle 的 DynamicProxy castleproject.org/projects/dynamicproxy

标签: c# .net reflection proxy


【解决方案1】:

看看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

【讨论】:

  • 这就是我一直在寻找的东西,并为我指明了正确的方向。谢谢。
  • 然而,一个重要的限制是封装到代理中的类型应该派生自 MarshalByRefObject。
  • @Piedone,要包装的类型应该是接口或派生自 MarshalByRefObject
  • 我花了几个小时使用这种方法实现代理,结果却遇到了编组错误。除非您可以重新构建类层次结构以从 MarshalByRefObject 派生,否则这没有用。
  • 对于 .NET Core:stackoverflow.com/q/38467753
【解决方案2】:

我不建议这样做。通常你会使用一些著名的库,例如 Castle 或 EntLib。对于一些复杂的类,动态生成代理可能是一个相当大的挑战。这是一个使用“Is”多态性的示例。为此,您必须将 base 中的所有方法声明为虚拟方法。您尝试执行此操作的方式(“有”)也是可能的,但对我来说看起来更复杂。

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

【讨论】:

    【解决方案3】:

    您可以按照in this question 的描述使用动态对象,但是对于动态生成的强类型对象,您必须使用Reflection.Emit,正如您所怀疑的那样。 This blog 的示例代码显示了类型的动态创建和实例化。

    我读到Roslyn 具有使创建动态代理更容易的功能,所以也可以看看那里。

    【讨论】:

      【解决方案4】:

      也许我误解了这个问题,构造函数怎么样?

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-10
        • 2010-10-03
        • 2010-11-28
        • 2020-01-13
        • 2011-06-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多