How to make a simple dynamic proxy in C#

2019-01-16 09:26发布

问题:

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.

回答1:

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 off 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 opensourced it here.



回答2:

In addition to Castle.DynamicProxy, there is also LinFu.DynamicProxy on Github.



回答3:

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. Real Proxy 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.

By the way, the second approach has a considerable limitation, you can't proxy non virtual methods.



回答4:

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.



回答5:

Here is simple example Dynamic Proxy Creation Using C# Emit

You can also use an AOP framework like PostSharp



回答6:

Another option is ContextBoundObject.

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



回答7:

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