web-dev-qa-db-fra.com

Comment faire un simple proxy dynamique en C #

Je veux créer un objet proxy dynamique pour ajouter certaines fonctionnalités à un objet.

en gros, je veux recevoir un objet, l'envelopper avec un objet qui ressemble à l'original que j'ai reçu et intercepter tous les appels.

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
    }

}

Juste pour clarifier, je veux faire quelque chose de similaire à l'usine de canaux WCF ...


J'ajoute une prime, car j'ai besoin d'un bon moyen pour les classes proxy (pas les interfaces) et pour gérer les méthodes non virtuelles (comme si j'avais hérité et ajouté un methond sous le "nouveau" mot clé). Je suis sûr que tout cela est très possible comme le fait le .Net.

49
AK_

J'aurais dû écrire cela plus tôt, mais tant pis.

Mon problème avait un "gotcha" spécial dont j'avais besoin pour pouvoir utiliser des classes proxy et non des interfaces.

Il existe deux solutions:

  1. Real Proxy et amis, signifie essentiellement utiliser .Net Remoting. Nécessite un pour hériter de ContextBoundObject.

  2. Construire un proxy en utilisant System.Reflection.Emit comme fait par spring vous pouvez également consulter le code de leur ProxyFactoryObject . Voici n autre trois articles sur le sujet .

Par ailleurs, la deuxième approche a une limitation considérable, vous ne pouvez pas proxy des méthodes non virtuelles.

14
AK_

Vous pouvez le faire avec une combinaison de DynamicObject et ImpromptuInterface mais vous devrez avoir une interface qui implémente les fonctions et les propriétés que vous souhaitez 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;
        }
    }
}

Vous pouvez bien sûr choisir de perdre la sécurité de type et aller avec un DynamicObject comme je l'ai montré, puis abandonner le casting de canard.

J'ai fait une version extensible transparente de ce proxy d'objet, et je l'ai ouvert ici .

36
albertjan

En plus de Castle.DynamicProxy , il y a aussi LinFu.DynamicProxy on Github .

14
Harry Steinhilber

Jetez un oeil à PostSharp . Je ne connais pas de moyen de faire ce que vous voulez dans Vanilla .Net, mais PostSharp propose des choses comme "OnMethodBoundaryAspect" qui peuvent être utilisées pour remplacer ou encapsuler le code à l'intérieur de la méthode.

Je l'ai utilisé pour faire des choses comme la journalisation, la validation des paramètres, la gestion des exceptions, etc.

Il existe une édition communautaire gratuite, qui devrait fonctionner pour vous. Vous en aurez besoin installé sur votre machine de développement, ainsi que sur tout serveur de build que vous utilisez.

5
NeilD

Une autre option est ContextBoundObject .

Il y a environ 8 à 9 ans, un article sur CodeProject utilisait cette approche pour tracer les appels de méthode.

2
leppie

Pour ajouter des fonctionnalités avant et après chaque fonction d'une classe, Real proxy est une bonne approche.

Alors maintenant, en T peut être n'importe quelle classe de test. Créer une instance comme celle-ci pour TestClass-

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

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