Как сделать простой динамический прокси в C # - PullRequest
49 голосов
/ 05 декабря 2011

Я хочу построить динамический прокси-объект, чтобы добавить определенную функциональность к объекту.

В основном я хочу получить объект, обернуть его объектом, который выглядит идентично оригиналу, который я получил, и перехватить всезвонки.

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 делает это.

Ответы [ 7 ]

36 голосов
/ 05 декабря 2011

Вы можете сделать это с помощью комбинации 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, как я показал, а затем отбросить кастинг.

Я сделал прозрачную расширяемую версию этого прокси-объекта и открыл ее здесь .

14 голосов
/ 12 января 2012

Я должен был написать это раньше, но не берите в голову.

В моем выпуске была специальная "ошибка", мне нужно было иметь возможность прокси-классов, а не интерфейсов.

Для этого есть два решения:

  1. Реальный прокси и друзья, в основном означает использование .Net Remoting. Требуется один для наследования от ContextBoundObject.

  2. Создание прокси с использованием System.Reflection.Emit , как это сделано spring . Вы также можете посмотреть код их ProxyFactoryObject . Вот еще три статьи по теме .

Кстати, второй подход имеет значительное ограничение, вы не можете использовать прокси не виртуальные методы.

14 голосов
/ 23 декабря 2011

В дополнение к Castle.DynamicProxy , есть также LinFu.DynamicProxy на Github .

5 голосов
/ 05 декабря 2011

Взгляните на PostSharp .Я не знаю, как сделать то, что вам нужно, в vanilla .Net, но PostSharp предлагает такие вещи, как «OnMethodBoundaryAspect», которые можно использовать для замены или переноса кода внутри метода.

Я использовал его для ведения журналов, проверки параметров, обработки исключений и т. Д.

Существует бесплатная версия Community Edition, которая должна работать для вас.Он понадобится вам на вашем компьютере для разработки и на любом используемом вами сервере сборки.

3 голосов
/ 05 декабря 2011

Вот простой пример Создание динамического прокси с использованием C # Emit

Вы также можете использовать AOP Framework, как PostSharp

2 голосов
/ 11 января 2012

Другой вариант: ContextBoundObject.

В CodeProject была статья 8-9 лет назад, использующая этот подход для отслеживания вызовов методов.

0 голосов
/ 10 января 2018

Для добавления любой функциональности до и после каждой функции в классе, реальный прокси является хорошим подходом.

Так что теперь в T может быть любой TestClass.Создайте подобный экземпляр для TestClass-

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

Код для 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
            {
            }
        }
    }
...