简体   繁体   English

如何在C#中做一个简单的动态代理

[英]How to make a simple dynamic proxy in C#

I want to build a dynamic proxy object to add certain functionality to an object.我想构建一个动态代理 object 来为 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.基本上我想收到一个 object,用一个看起来与我得到的原始相同的 object 包装它,并拦截所有呼叫。

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...澄清一下,我想做一些类似于 WCF 通道工厂的事情......


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).我正在添加赏金,因为我需要一种代理类(而不是接口)和处理非虚拟方法的好方法(就像我在“new”关键字下继承并添加了一个方法一样)。 I'm sure all this is very possible as the.Net does it.我确信这一切都是非常有可能的,因为.Net 做到了。

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.您可以使用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;
        }
    }
}

You could of course choose to lose the type-safety and go with a DynamicObject like I showed and then drop the duck-casting.你当然可以选择失去类型安全性并使用我展示的DynamicObject ,然后放弃鸭子铸造。

I made a transparant extendible version of this object proxy, and open-sourced it here .我制作了这个对象代理的透明可扩展版本,并在此处开源。

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. RealProxy和朋友们,基本上意味着使用 .NET Remoting。 Requires one to inherit from ContextBoundObject .需要一个从ContextBoundObject继承。

  2. Building a proxy using System.Reflection.Emit as done by spring you can also look at the code of their ProxyFactoryObject .spring一样使用System.Reflection.Emit构建代理,您还可以查看其ProxyFactoryObject的代码。 Here are another three articles on the subject .这里是关于这个主题的另外三篇文章

    • This approach has the crucial disadvantage of limiting you to overriding only virtual members.这种方法的主要缺点是限制您只能覆盖virtual成员。

Take a look at PostSharp .看看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.我不知道有什么方法可以在 vanilla .Net 中执行您想要的操作,但是 PostSharp 提供了诸如“OnMethodBoundaryAspect”之类的东西,可用于替换或包装方法中的代码。

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.您需要将它安装在您的开发机器以及您使用的任何构建服务器上。

Another option is ContextBoundObject .另一种选择是ContextBoundObject

There was an article on CodeProject about 8-9 years back using this approach to trace method calls.大约 8-9 年前,有一篇关于 CodeProject 的文章使用这种方法来跟踪方法调用。

For adding any functionality before and after of every function in a class, Real proxy is a good approach.为了在类中的每个函数之前和之后添加任何功能,Real proxy 是一个很好的方法。

So now in T can be any TestClass.所以现在在 T 中可以是任何 TestClass。 Create Instance like this for TestClass-为 TestClass 创建这样的实例-

var _instance=(object)DynamicProxy(TestClass).GetTransparentProxy(); 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
            {
            }
        }
    }

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.除非您让 CLR 挂钩到对该对象的每个方法/属性调用并将调用重定向到您创建的虚假成员,否则您无法拦截对静态成员、非虚拟成员或私有成员的所有调用。 You can achieve that by using the .NET Profiler API .您可以通过使用.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.例如,TypeMock Isolator 使用它来监视应用程序的执行,并且在调用方法时,CLR 会通知 typemock 隔离器,这允许 Isolator 完全覆盖原始类。

.NET 6.0 has added a new candidate to the Reflection namespace: the DispatchProxy . .NET 6.0 向Reflection命名空间添加了一个新的候选对象: DispatchProxy The team is announcing it here .团队在这里宣布。 A sample usage is contained in the article.文章中包含一个示例用法。

You could do this with just DynamicObject from System.Danymic namespace, without using any third party libraries.您可以仅使用 System.Danymic 命名空间中的 DynamicObject 来执行此操作,而无需使用任何第三方库。

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:然后在驱动 class 上:

dynamic dynamicProxy = new DynamicProxy(person);

That way, you can set and get the fields with access control.这样,您可以设置和获取具有访问控制的字段。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM