ti-enxame.com

Como criar um proxy dinâmico simples em c #

Eu quero criar um objeto proxy dinâmico para adicionar certas funcionalidades a um objeto.

basicamente, quero receber um objeto, envolvê-lo com um objeto que pareça idêntico ao original que recebi e interceptar todas as chamadas.

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
    }

}

Só para esclarecer, quero fazer algo semelhante à fábrica de canais WCF ...


Estou adicionando uma recompensa, porque preciso de uma boa maneira de proxy de classes (não interfaces) e de lidar com métodos não virtuais (como se eu tivesse herdado e adicionado um método na palavra-chave "new"). Tenho certeza de que tudo isso é muito possível, como o .Net faz.

49
AK_

Eu deveria ter escrito isso antes, mas não importa.

Meu problema tinha uma "pegadinha" especial que eu precisava para poder proxyear classes e não interfaces.

Existem duas soluções para isso:

  1. Proxy Real e amigos, basicamente significa usar o .Net Remoting. Requer um para herdar de ContextBoundObject.

  2. Construindo um proxy usando System.Reflection.Emit , como feito por spring , você também pode ver o código de seus ProxyFactoryObject . Aqui estão outro três artigos no assunto .

A propósito, a segunda abordagem tem uma limitação considerável, você não pode usar proxy de métodos não virtuais.

14
AK_

Você pode fazer isso com uma combinação de DynamicObject e ImpromptuInterface mas precisará ter uma Interface que implemente as funções e propriedades que deseja 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;
        }
    }
}

É claro que você pode optar por perder a segurança de tipo e seguir com um DynamicObject como eu mostrei e depois soltar o pato.

Eu criei uma versão extensível transparente desse proxy de objeto e o enviei aqui .

36
albertjan

Além de Castle.DynamicProxy , também há LinFu.DynamicProxy no Github .

14
Harry Steinhilber

Dê uma olhada em PostSharp . Não sei como fazer o que você quer no Vanilla .Net, mas o PostSharp oferece coisas como "OnMethodBoundaryAspect", que podem ser usadas para substituir ou quebrar o código dentro do método.

Eu o usei para fazer coisas como registro, validação de parâmetros, tratamento de exceções etc.

Existe uma edição comunitária gratuita, que deve funcionar para você. Você precisará dele instalado em sua máquina de desenvolvimento, bem como em qualquer servidor de construção que você use.

5
NeilD

Outra opção é ContextBoundObject .

Havia um artigo no CodeProject cerca de 8 a 9 anos atrás, usando essa abordagem para rastrear chamadas de método.

2
leppie

Para adicionar qualquer funcionalidade antes e depois de todas as funções de uma classe, o proxy Real é uma boa abordagem.

Então agora em T pode haver qualquer TestClass. Crie uma instância como esta para TestClass-

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

O código para Proxy Dinâmico

 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
            {
            }
        }
    }
0
Ankita Sachan