如何用C#制作一个简单的动态代理

发布于 2024-12-19 09:19:41 字数 565 浏览 1 评论 0 原文

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

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

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 就是这么做的。

I want to build a dynamic proxy object to add certain functionality to an object.

basically i want to receive an object, wrap it with an object that looks identical to the original i got, and intercept all the calls.

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
    }

}

Just to clarify, I want to do something similar to the WCF channel factory...


I'm adding a bounty, because I need a good way to proxy classes (not interfaces) and to handle non virtual methods (as if I inherited and added a methond under the "new" keyword).
I'm sure all this is very possible as the .Net does it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

岁月静好 2024-12-26 09:19:42

我应该早点写这篇文章,但没关系。

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

有两种解决方案:

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

  2. 使用 System.Reflection.Emit 构建代理spring 你也可以看看他们的代码 ProxyFactoryObject。这里有另外三个关于 href="http://weblogs.asp.net/seanmcalinden/archive/2010/03/18/creating-a-dynamic-proxy-generator-part-1-creating-the- assembly-builder-module-builder-and -caching-mechanism.aspx" rel="nofollow noreferrer">主题

    • 这种方法有一个严重的缺点,即限制您只能覆盖虚拟成员。

I should have written this sooner, but never mind.

My issue had a special "gotcha" I needed to be able to proxy classes and not interfaces.

There are two solutions to this:

  1. RealProxy and friends, basically means using .NET Remoting. Requires one to inherit from ContextBoundObject.

  2. Building a proxy using System.Reflection.Emit as done by spring you can also look at the code of their ProxyFactoryObject. Here are another three articles on the subject.

    • This approach has the crucial disadvantage of limiting you to overriding only virtual members.
甜妞爱困 2024-12-26 09:19:42

.NET 6.0 向 Reflection 命名空间添加了一个新的候选者:DispatchProxy。该团队在此处宣布这一消息。文章中包含示例用法。

.NET 6.0 has added a new candidate to the Reflection namespace: the DispatchProxy. The team is announcing it here. A sample usage is contained in the article.

很糊涂小朋友 2024-12-26 09:19:42

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

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

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

Take a look at PostSharp.
I don't know of a way to do what you want in vanilla .Net, but PostSharp offers things like "OnMethodBoundaryAspect" which can be used to either replace or wrap the code inside the method.

I've used it to do things like logging, parameter validation, exception handling etc.

There is a free Community Edition, which should work for you. You'll need it installed on your development machine, as well as any build server that you use.

天涯沦落人 2024-12-26 09:19:42

另一个选项是 ContextBoundObject

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

Another option is ContextBoundObject.

There was an article on CodeProject about 8-9 years back using this approach to trace method calls.

自演自醉 2024-12-26 09:19:42

您可以仅使用 System.Danymic 命名空间中的 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);

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

You could do this with just DynamicObject from System.Danymic namespace, without using any third party libraries.

Example code:

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"];
        }
}

Then on the driver class:

dynamic dynamicProxy = new DynamicProxy(person);

That way, you can set and get the fields with access control.

|煩躁 2024-12-26 09:19:42

要在类中的每个函数之前和之后添加任何功能,真实代理是一个很好的方法。

所以现在 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
            {
            }
        }
    }

For adding any functionality before and after of every function in a class, Real proxy is a good approach.

So now in T can be any TestClass. Create Instance like this for TestClass-

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

The code for Dynamic Proxy-

 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
            {
            }
        }
    }
梦纸 2024-12-26 09:19:42

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

You can't intercept all calls for static, not virtual, or private members unless you get the CLR to hooks into each every method/property call to that object and redirect call to fake one you created. You can achieve that by using the .NET Profiler API. TypeMock Isolator for example uses it monitor an application's execution and when method is called, CLR notifies typemock isolator which allows Isolator to override the original class completely.

烙印 2024-12-26 09:19:41

您可以结合使用 DynamicObject 和 < a href="https://github.com/ekonbenefits/impromptu-interface" rel="noreferrer">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 ,然后放弃鸭子铸造。

我制作了该对象代理的透明可扩展版本,并在此处将其开源。

You could do this with a combination of DynamicObject and ImpromptuInterface but you will have to have an Interface that implements the functions and properties you want to proxy.

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

You could of course choose to lose the type-safety and go with a DynamicObject like I showed and then drop the duck-casting.

I made a transparant extendible version of this object proxy, and open-sourced it here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文