如何在c#中创建一个简单的动态代理



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

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

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做到了。

你可以使用DynamicObject和ImpromptuInterface的组合来实现这一点,但是你必须有一个接口来实现你想要代理的函数和属性。

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,然后放弃duck-casting。

我为这个对象代理做了一个透明的可扩展版本,并在这里开源了它。

除了Castle。动态代理,也有林甫。

我应该早点写的,不过没关系。

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

有两个解决方案:

  1. RealProxy和朋友,基本上意味着使用。net Remoting。需要从ContextBoundObject继承。

      这种方法利用了。net JIT编译器提供的"魔力"(这是硬编码的,专门识别RealProxy),让您"覆盖"非虚拟成员。
  2. 使用System.Reflection.Emit构建代理,正如spring所做的那样,你也可以看看它们的ProxyFactoryObject的代码。这里是关于这个主题的另外三篇文章。

    • 这种方法有一个关键的缺点,那就是你只能覆盖virtual成员。

    。. NET 6.0为Reflection名称空间添加了一个新的候选名称空间:DispatchProxy。团队在这里宣布。本文中包含一个示例用法。

    看看PostSharp。我不知道有什么方法可以在。net中实现你想要的,但是PostSharp提供了像OnMethodBoundaryAspect"可用于替换或包装方法中的代码。

    我用它来做日志,参数验证,异常处理等。

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

    另一个选项是ContextBoundObject

    大约8-9年前,CodeProject上有一篇文章使用这种方法跟踪方法调用。

    对于在类中的每个函数之前和之后添加任何功能,Real proxy是一种很好的方法。

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

    var _instance =(对象)了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
                {
                }
            }
        }
    

    你不能拦截对静态成员的所有调用,而不是对虚拟成员或私有成员的调用,除非你让CLR钩子到对该对象的每个方法/属性调用中,并将调用重定向到你创建的对象。你可以通过使用。net Profiler API来实现。例如,TypeMock隔离器使用它监视应用程序的执行,当方法被调用时,CLR通知TypeMock隔离器,这允许隔离器完全覆盖原始类。

    您可以使用来自System的DynamicObject来完成此操作。命名空间,不使用任何第三方库。

    示例代码:

    public class DynamicProxy: DynamicObject
        {
            private readonly T _object;
            // The inner dictionary.
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            // Getting a property.
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                return dictionary.TryGetValue(binder.Name, out result);
            }
            // Setting a property.
            // You can set up access control eg. if you don't want to 
            // set certain field, you can return false before putting
            // the value into the inner dictionary
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                if (binder.Name.Equals("Rating")) return false;
                dictionary[binder.Name] = value;
                return true;
            }
            public DynamicProxy(T object)
            {
                _object = object;
                dictionary["Name"] = object.GetName();
                dictionary["Gender"] = object.GetGender();
                dictionary["Interests"] = object.GetInterests();
                dictionary["Rating"] = object.GetGeekRating();
            }
            public string GetName()
            {
                return (string)dictionary["Name"];
            }
            public int GetGeekRating()
            {
                return (int)dictionary["Rating"];
            }
    }
    

    然后在驱动类上:

    dynamic dynamicProxy = new DynamicProxy(person);
    

    这样,您就可以设置和获取具有访问控制的字段。

    相关内容

    • 没有找到相关文章

    最新更新