【问题标题】:How to make a simple dynamic proxy in C#如何在 C# 中制作一个简单的动态代理
【发布时间】:2012-01-13 07:02:08
【问题描述】:

我想构建一个动态代理对象来为对象添加某些功能。

基本上我想接收一个对象,用一个看起来与我得到的原始对象相同的对象包装它,然后拦截所有调用。

class Wrapper : DynamicProxy// dynamic proxy is not a reall class, but i guess something like this exists...
{
    public static T Wrap(T obj)
    {
        return (T) new Wrapper(obj);
    }

    public override object InterceptCall(MethodInfo info, object[] args)
    {
        // do stuff
    }

}

澄清一下,我想做一些类似于 WCF 通道工厂的事情...


我正在添加赏金,因为我需要一种代理类(而不是接口)和处理非虚拟方法的好方法(就像我在“new”关键字下继承并添加了一个方法一样)。 我确信这一切都是 .Net 实现的。

【问题讨论】:

  • @np-hard 如果您将此作为答案发布,我会接受...不是 100% 我想要的,但已经足够了。
  • 你知道要在编译时代理的类型,还是只在运行时代理?
  • @Sneal 仅在运行时。可能我会在编译时知道它,但我想保持灵活性......
  • 这肯定会让事情变得更加困难,因为 Castle DynamicProxy 只能拦截虚拟方法。我认为您无法使用分析 api。

标签: c# .net reflection proxy aop


【解决方案1】:

看看PostSharp。 我不知道在 vanilla .Net 中做你想做的事情的方法,但是 PostSharp 提供了诸如“OnMethodBoundaryAspect”之类的东西,可以用来替换或包装方法内的代码。

我用它来做一些事情,比如日志记录、参数验证、异常处理等。

有一个免费的社区版,应该适合您。您需要将它安装在您的开发机器以及您使用的任何构建服务器上。

【讨论】:

  • Postsharp 在编译时为已知类型工作。我不认为是 OP 需要的
【解决方案2】:

您可以使用DynamicObjectImpromptuInterface 的组合来执行此操作,但您必须有一个接口来实现您要代理的功能和属性。

public interface IDoStuff
{
    void Foo();
}

public class Wrapper<T> : DynamicObject
{
    private readonly T _wrappedObject;

    public static T1 Wrap<T1>(T obj) where T1 : class
    {
        if (!typeof(T1).IsInterface)
            throw new ArgumentException("T1 must be an Interface");

        return new Wrapper<T>(obj).ActLike<T1>();
    }

    //you can make the contructor private so you are forced to use the Wrap method.
    private Wrapper(T obj)
    {
        _wrappedObject = obj;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        try
        {
            //do stuff here

            //call _wrappedObject object
            result = _wrappedObject.GetType().GetMethod(binder.Name).Invoke(_wrappedObject, args);
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
}

您当然可以选择失去类型安全性并像我展示的那样使用DynamicObject,然后放弃鸭子铸造。

我制作了这个对象代理的透明可扩展版本,并将其开源here

【讨论】:

  • 您能简单谈谈 ImpromptuInterface 的幕后工作原理吗?也就是说,ActLike 使用什么技术来创建任何类型的对象?某种字节码生成?
  • @Lii 我没有开发 ImpromptuInterface。但它使用 DLR 动态绑定到鸭类型对象上的函数。然后它使用相同的技巧将其转换为您指定的接口。
  • 这样的事情对性能有什么影响? AFAIK .GetType() 对性能不太友好。
  • 是的,检查构造类型并将所有函数放入字典中是一个聪明的主意。嗯,我的解释是为了简单而不是性能。
  • @FrankerZ 如果你在这里查看我的项目:github.com/curit/DynamicProxy/blob/master/DynamicProxy/… 你会发现我在那里做了不同的事情。 :)
【解决方案3】:

【讨论】:

    【解决方案4】:

    另一个选项是ContextBoundObject

    大约 8-9 年前有一篇关于 CodeProject 的文章使用这种方法来跟踪方法调用。

    【讨论】:

      【解决方案5】:

      我应该早点写这个,但没关系。

      我的问题有一个特殊的“陷阱”,我需要能够代理类而不是接口。

      有两种解决方案:

      1. RealProxy 和朋友们,基本上是指使用.NET Remoting。需要一个继承自ContextBoundObject

      2. 使用System.Reflection.Emit 构建代理,正如spring 所做的那样,您还可以查看他们的ProxyFactoryObject 的代码。这里是another 三个articles subject

        • 这种方法的一个关键缺点是限制您只能覆盖 virtual 成员。

      【讨论】:

      • 没有。但是你可以通过链接找到比我在这里写的任何东西更好的例子和解释。
      【解决方案6】:

      要在类中的每个函数之前和之后添加任何功能,Real proxy 是一个不错的方法。

      所以现在在 T 中可以是任何 TestClass。像这样为TestClass创建实例-

      var _instance=(object)DynamicProxy(TestClass).GetTransparentProxy();

      动态代理的代码-

       class DynamicProxy<T> : RealProxy
          {
              readonly T decorated;
      
              public DynamicProxy(T decorated) : base(typeof(T))
              {
                  this.decorated = decorated;
              }
      
              public override IMessage Invoke(IMessage msg)
              {
                  var methodCall = msg as IMethodCallMessage;
                  var methodInfo = methodCall.MethodBase as MethodInfo;
                  string fullMethodName = $"{methodInfo.DeclaringType.Name}.{methodCall.MethodName}";
      
                  try
                  {
                      var result = methodInfo.Invoke(decorated, methodCall.InArgs);
      
                      return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
                  }
      
                  catch (Exception e)
                  {
                      return new ReturnMessage(e, methodCall);
                  }
                  finally
                  {
                  }
              }
          }
      

      【讨论】:

        【解决方案7】:

        除非您让 CLR 挂钩到对该对象的每个方法/属性调用并将调用重定向到您创建的假对象,否则您无法拦截对静态、非虚拟或私有成员的所有调用。您可以使用.NET Profiler API 来实现这一点。例如,TypeMock Isolator 使用它来监视应用程序的执行,当方法被调用时,CLR 会通知 typemock 隔离器,它允许 Isolator 完全覆盖原始类。

        【讨论】:

          【解决方案8】:

          .NET 6.0 向 Reflection 命名空间添加了一个新候选对象:DispatchProxy。该团队正在宣布它here。文章中包含一个示例用法。

          【讨论】:

            猜你喜欢
            • 2023-04-07
            • 2010-09-18
            • 2011-01-29
            • 2015-11-23
            • 2010-11-19
            • 2012-12-19
            • 2010-12-20
            相关资源
            最近更新 更多