Codingly

Utiliser l’AOP avec PostSharp pour implémenter INotifyPropertyChanged

Posted in Articles by Romain Verdier on octobre 29, 2008

J’ai ce billet dans mes drafts depuis un moment, et il serait peut-être bon de l’en sortir avant qu’il ne soit trop tard… Dans un post récent, je parlais de l’interface INotifyPropertyChanged, en donnant un exemple d’implémentation basée sur les lambda expressions et les expression trees qui permettait d’obtenir une solution fortement typée.

L’AOP peut également être une solution, et peut-être même le meilleur compromis pour peu que l’on en tire partie correctement. Suite à un commentaire de Jb, j’ai essayé de voir ce que pouvait donner l’utilisation d’un tisseur statique comme PostSharp dans ce cas précis.

J’avais déjà fait auparavant quelques tests satisfaisants avec DynamicProxy de Castle, qui est un, heu, tisseur dynamique. Les contraintes m’avaient semblées être assez fortes (toujours pour résoudre la problématique de INotifyPropertyChanged) :

  • Nécessité de créer un proxy pour chaque instance d’objet bindé
  • Interception uniquement possible sur les propriétés virtuelles
  • Performances moyennes dues au caractère dynamique des interceptions

Cela dit, en terme de concision, c’est probablement la méthode la plus efficace puisqu’en créant un StandardInterceptor d’une dizaine de lignes on pouvait s’en sortir.

Revenons à PostSharp. J’ai été impressionné, vraiment, par cet outil. Sans passer trop de temps à expliquer son fonctionnement, puisque Gael Fraiteur l’a déjà fait, sachez que le framework est un static weaver (entre autres), qui intervient en temps que post-compiler pour inspecter et modifier le code IL des assemblies résultant de la compilation. Je vous invite à consulter les ressources mises à disposition par l’auteur sur le site, elles sont assez complètes. Dans le contexte de notre problématique (oui, les notifications m’obsèdent) les avantages du tissage en post-compilation sont les suivants :

  • Pas de proxy à créer au runtime pour chaque instance
  • Les types sur lesquels on désire tisser l’aspect « notification-des-changements » n’ont pas besoin de respecter une quelconque convention
  • Une partie du travail peut être faite durant le build process, ce qui améliore les performances au runtime.

Illustrons par le code.

Voici mon POCO (je vous conseille d’imaginer le même avec 15 propriétés) :

    public class Person
    {
        private string name;

        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
    }

Voici ce que je cherche à obtenir, fonctionnellement, sans avoir à écrire le code de plomberie :

    public class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string name;

        public string Name
        {
            get { return this.name; }
            set
            {
                if (this.name != value)
                {
                    this.name = value;
                    FirePropertyChanged("Name");
                }
            }
        }

        private void FirePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

En fait, voici ce que je cherche réellement à obtenir :

    [NotifyPropertyChanged]
    public class Person
    {
        private string name;

        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
    }

La différence avec la première version est subtile : un attribut NotifyPropertyChanged. Je veux que tout soit transparent au niveau de mon code client. Que des instances de Person puissent être créées, ajoutées à des sources de données, modifiées comme n’importe quelles autres instances, etc.

Donc, exit le proxying, exit l’interception dynamique : ceux qui y sont habitués ont déjà compris que ce n’était pas possible. C’est là que réside la force d’un tisseur comme PostSharp (ou AspectDNG) : ils rendent possible ce genre de choses, grâce à l’approche potentiellement troublante du tissage statique.

Pour la suite de l’article, je vais simplement vous montrer une solution, et la commenter. Il ne s’agit pas d’un tutorial – mais plutôt d’une pseudo-étude de cas ayant pour but de donner un bon aperçu des capacités de PostSharp. Et accessoirement d’apaiser mon obsession.

Le principe

Utiliser PostSharp est un peu moins intuitif qu’utiliser DynamicProxy. Je vais tenter de vous exposer le principe de base de la solution.

Que faut-il pour passer d’un POCO à un POCO qui supporte INotifyPropertyChanged :

  1. Implémenter l’interface du même nom : INotifyPropertyChanged, et donc, fournir un event public ayant la signature suivante : public event PropertyChangedEventHandler PropertyChanged;
  2. Intercepter les accès en écriture aux propriétés, pour déclencher cet évènement PropertyChanged. Dans l’exemple donné plus haut, on va même jusqu’à tester s’il est nécessaire de déclencher l’évènement en comparant la nouvelle valeur à l’existante.

Ce sont deux choses distinctes, bien qu’on ait souvent tendance à les associer, ne serait-ce que parce que c’est logique. Mais pour bien comprendre comment utiliser l’AOP, et plus particulièrement comment concevoir l’aspect que l’on va vouloir tisser grâce à PostSharp, c’est important. Car nous allons en fait utiliser un aspect composite, formé de plusieurs sous-aspects ayant chacun un but précis.

Interception des affectations des champs privés

Ce sous-aspect, de type OnFieldAccessAspect, a un rôle facultatif : il va vérifier que la valeur que l’on cherche à affecter à un champ est différente de la valeur existante. Cela permettra de savoir si l’on doit ou non déclencher l’évènement PropertyChanged.

Implémentation de INotifyPropertyChanged

Le but de ce sous-aspect et d’implémenter INotifyPropertyChanged. PostSharp propose un CompositionAspect qui, lorsqu’il est appliqué à un type, se charge de lui faire implémenter une interface donnée en utilisant la composition et la délégation.

Le POCO implémentera bien l’interface INotifyPropertyChanged, mais les abonnements/désabonnements à son évènement PropertyChanged seront interceptés et relayés à un composant (InnerNotifier ci-dessus) implémentant également INotifyPropertyChanged. Quelque chose du genre :

    public event PropertyChangedEventHandler PropertyChanged
    {
        add { this.innerNotifier.PropertyChanged += value; }
        remove { this.innerNotifier.PropertyChanged -= value; }
    }

Le CompositionAspect a donc deux tâches :

  1. Définir quelle interface le POCO doit implémenter. Une petite idée ?
  2. Fournir un composant qui se chargera de la réelle logique d’implémentation de l’interface, et qui, nous le verrons plus tard, offrira également un moyen de déclencher l’évènement PropertyChanged (méthode FirePropertyChanged ci-dessus)

Interception des appels aux setters des propriétés

Ce sous-aspect a pour mission de déclencher l’évènement PropertyChanged lorsqu’un setter est appelé et que le backing field a bien été modifié. Il doit donc interagir, d’une manière ou d’une autre, avec l’aspect précédent pour déclencher l’évènement.

Il est de type OnMethodBoundaryAspect selon l’API PostSharp.

Composition de l’aspect composite

Enfin, il faut l’aspect de haut niveau, qui agrège les sous-aspects de façon cohérente pour offrir la fonctionnalité désirée. PostSharp permet la définition d’aspects composites : il suffit de créer un CompoundAspect.

La solution

Si l’approche down-top correspondait plutôt bien à l’exposition du principe de fonctionnement, je crois qu’il est plus clair ici de partir du plus haut niveau pour découvrir l’implémentation de la solution proposée.

Et le point de départ, c’est l’attribut [NotifyPropertyChanged] dont on souhaitait décorer les POCO à tisser. En voici le code :

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
[MulticastAttributeUsage(MulticastTargets.Class, AllowMultiple = false)]
public class NotifyPropertyChangedAttribute : CompoundAspect
{
    public override void ProvideAspects(object element,
                                        LaosReflectionAspectCollection collection)
    {
        var type = (Type)element;
        collection.AddAspect(type, new ImplementINotifyPropertyChangedAspect());

        BindingFlags lookupFlags = BindingFlags.Instance
                                 | BindingFlags.Public
                                 | BindingFlags.DeclaredOnly;
        foreach (PropertyInfo propertyInfo in type.GetProperties(lookupFlags))
        {
            if (propertyInfo.CanWrite)
            {
                MethodInfo setterInfo = propertyInfo.GetSetMethod();
                collection.AddAspect(setterInfo, 
                                     new FirePropertyChangedAspect(setterInfo.Name));
            }
        }

        lookupFlags = BindingFlags.Instance
                    | BindingFlags.NonPublic
                    | BindingFlags.DeclaredOnly;
        foreach (FieldInfo fieldInfo in type.GetFields(lookupFlags))
        {
            collection.AddAspect(fieldInfo, new InterceptFieldAffectationAspect());
        }
    }
}

C’est l’aspect composé lui-même, le CompoundAspect. Analysons-le un peu plus en détails :

  • Il est lui-même décoré par des attributs définissant les cibles possibles de l’aspect. Ici, on vise les types. PostSharp permet aussi l’application d’aspects par multicasting, ce qui permet de tisser des aspects sans la moindre intrusion au niveau du code à instrumenter. Concrètement, on pourrait se passer d’appliquer manuellement notre attribut [NotifyPropertyChanged] sur chaque type, et définir à la place une règle déterminant quels sont les types sur lesquels appliquer l’aspect.
  • La méthode ProvideAspects est exécutée lors de la post-compilation. Elle permet de définir quels sont les sous-aspects qui composent le CompoundAspect, et sur quoi ils s’appliquent. Chaque sous-aspect doit être ajouté avec le descripteur de sa cible. Nous allons retrouver les 3 types d’aspect sus-cités :
    • Le sous aspect ImplementINotifyPropertyChangedAspect est appliqué au type de l’élément que l’on tisse.
    • Le sous aspect FirePropertyChangedAspect concerne les setters du type. On en ajoute donc une instance pour chacun d’entre eux.
    • Enfin, le sous aspect InterceptFieldAffectationAspect concerne les champs du type. On en ajoute une instance pour chacun des champs privés du type.

Regardons à quoi ressemble le premier des sous-aspects :

[Serializable]
internal class ImplementINotifyPropertyChangedAspect : CompositionAspect
{
    public override object CreateImplementationObject(InstanceBoundLaosEventArgs eventArgs)
    {
        return new InnerNotifier(eventArgs.Instance);
    }

    public override Type GetPublicInterface(Type containerType)
    {
        return typeof (INotifyPropertyChanged);
    }

    public override CompositionAspectOptions GetOptions()
    {
        return CompositionAspectOptions.GenerateImplementationAccessor;
    }
}

Ca colle assez bien avec les explications précédentes, et le code est auto commenté. Le composant implémentant réellement l’interface INotifyPropertyChanged est un InnerNotifier dont voici la définition :

[Serializable]
internal class InnerNotifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private readonly object eventSender;

    private bool shouldFirePropertyChanged;

    public InnerNotifier(object eventSender)
    {
        this.eventSender = eventSender;
    }
    
    public bool ShouldFirePropertyChanged
    {
        get { return this.shouldFirePropertyChanged; }
        set { this.shouldFirePropertyChanged = value; }
    }

    public void FirePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null && this.shouldFirePropertyChanged)
        {
            this.PropertyChanged(this.eventSender, 
                                 new PropertyChangedEventArgs(propertyName));
        }
    }
}

Là encore c’est trivial : on implémente l’interface, et on expose une méthode publique permettant de déclencher manuellement l’évènement.

C’est d’ailleurs l’aspect FirePropertyChangedAspect qui s’en chargera :

[Serializable]
public class FirePropertyChangedAspect : OnMethodBoundaryAspect
{
    private InnerNotifier notifier;
    private readonly string propertyName;

    public FirePropertyChangedAspect(string propertyName)
    {
        this.propertyName = propertyName;
    }

    public override void OnSuccess(MethodExecutionEventArgs eventArgs)
    {
        if (this.notifier == null)
        {
            var composedInteface = eventArgs.Instance as IComposed<INotifyPropertyChanged>;
            if (composedInteface != null)
            {
                this.notifier =
                    composedInteface.GetImplementation(eventArgs.InstanceCredentials)
                    as InnerNotifier;
            }
        }

        if (this.notifier != null)
        {
            this.notifier.FirePropertyChanged(this.propertyName);
        }
    }
}

La méthode OnSuccess est une des méthodes qu’il est possible de surdéfinir sur un OnMethodBoundaryAspect. Dans le flow d’interception, elle sera invoquée au runtime lorsque l’appel à la méthode cible aura réussi, donc, après que ce dernier ait eu lieu. Si vous êtes encore à me lire, vous méritez que je vous épargne les détails d’implémentation de cette méthode, mais sachez au moins qu’on récupére le InnerNotifier ayant été tissé par l’aspect ImplementINotifyPropertyChangedAspect, et que l’on appelle sa méthode FirePropertyChanged pour déclencher l’évenement en lui passant le nom de la propriété concernée.

Note: Fouillez dans la doc de PostSharp au sujet de IComposed<T> si vous voulez plus de viande, mais en gros, c’est le moyen d’exposer le composant d’un CompositionAspect (ici, notre InnerNotifier) lorsque l’option CompositionAspectOptions.GenerateImplementationAccessor a été définie. (cf. méthode GetOptions dans ImplementINotifyPropertyChangedAspect)

OK. Et non, ce n’est pas terminé, il reste bien le dernier sous-aspect utilisé pour déterminer si l’évenement PropertyChanged sera lancé ou non. J’ai nommé, InterceptFieldAffectationAspect :

[Serializable]
public class InterceptFieldAffectationAspect : OnFieldAccessAspect
{
    private InnerNotifier notifier;

    public override void OnSetValue(FieldAccessEventArgs eventArgs)
    {
        if (this.notifier == null)
        {
            var composedInteface = eventArgs.Instance as IComposed<INotifyPropertyChanged>;
            if (composedInteface != null)
            {
                this.notifier =
                    composedInteface.GetImplementation(eventArgs.InstanceCredentials)
                    as InnerNotifier;
            }
        }
        if (this.notifier != null)
        {
            this.notifier.ShouldFirePropertyChanged =
                !eventArgs.ExposedFieldValue.Equals(eventArgs.StoredFieldValue);
        }
        base.OnSetValue(eventArgs);
    }
}

Dans la méthode OnSetValue :

  • On récupère une fois de plus le InnerNotifier ayant été tissé par l’aspect ImplementINotifyPropertyChangedAspect
  • Si la valeur affectée au champ est égale à la valeur existante, on empêche le déclenchement de l’évènement via la propriété ShouldFirePropertyChanged du notifier.

Et voilà.

En résumé

Hum, pas vraiment un résumé, mais plutôt la séquence ayant lieu au runtime lors de la modification d’une propriété d’un objet instrumenté :

  1. On s’abonne à l’évenement PropertyChanged de l’objet : ce dernier propage l’abonnement à son InnerNotifier.
  2. On affecte une valeur à la propriété : le setter est appelé, mais l’interception par FirePropertyChangedAspect n’aura lieu qu’à la sortie de ce dernier.
  3. Dans le corps du setter, on affecte la valeur au backing field : c’est une affectation interceptée par l’aspect InterceptFieldAffectationAspect. Si la valeur affectée est différente de celle du champ, alors il autorise le InnerNotifier à déclencher l’évènement PropertyChanged via la propriété ShouldFirePropertyChanged. Sinon, il désactive l’InnerNotifier, toujours via la propriété ShouldFirePropertyChanged.
  4. A la sortie du setter, on entre dans le code d’interception de l’aspect FirePropertyChangedAspect qui, via l’InnerModifier, déclenche l’évènement PropertyChanged. Ce dernier ne sera effectivement déclenché que s’il a été laissé actif par l’InterceptFieldAffectationAspect précédemment.

Quelques remarques pour les plus courageux désoeuvrés :

  • L’implémentation n’est pas une référence, et la communication entre les différents sous-aspects via le InnerNotifier n’est pas quelque chose dont je suis particulièrement fier. Mais ma connaissance de PostSharp est trop limitée actuellement pour me permettre d’envisager quelque chose de plus élégant.
  • La solution peut sembler fastidieuse, mais compte tenu des spécifications (assez virtuelles, j’avoue), elle offre un bon compromis au niveau des performances : 5 fois plus lente que sans AOP, mais 5 fois plus rapide qu’une solution à base de tissage dynamique. Environ. Update : Il semblerait que dans le cas de cette implémentation les performances soient les mêmes que celles qu’on pourrait obtenir avec un tisseur dynamique.
  • Au niveau de l’utilisation, c’est la forme d’AOP la plus concise et la moins intrusive possible, donc celle qui offre potentiellement le meilleur niveau d’abstraction et le meilleur gain de productivité.
  • Si vous avez des alternatives, avec ou sans PostSharp, je suis preneur, évidemment.

Cet article est probablement trop chiant pour être lu jusqu’au bout, et trop spécifique pour être utile, mais il me fallait un post pour le mois d’octobre. Haha.

Tagged with: , , , ,

18 Réponses

Subscribe to comments with RSS.

  1. Romain Verdier said, on octobre 30, 2008 at 10:44

    Hum, j’ai trouvé un meilleur moyen (évident) d’appliquer l’aspect FirePropertyChangedAspect uniquement aux setters sans avoir à faire de pauvres tests sur le nom de la méthode, etc. Par la même occasion, on peut instancier l’aspect en question en lui passant directement le nom de la propriété, pour lui éviter d’avoir à le résoudre.

    Je mettrai à jour l’article ce soir.

  2. grozeille said, on octobre 30, 2008 at 4:48

    lol ouai il est long à lire, j’ai zapé après 3 scrolls de souris je crois…
    ça a le mérite de me faire découvrir Postsharp… par contre je n’ai pas trop saisie (ouai, trop fatigué à 18h…) pourquoi c’est plus lent que de coder le INotifyPropertyChanged soit même? OK pour le tisseur dynamique, mais si le statique intervient post-compilation, pourquoi son code serai moins optimisé que notre code fait à la main?

    En tout cas, bravo! tu as fait ton poste d’octobre ;)

  3. Romain Verdier said, on octobre 30, 2008 at 7:30

    Je suis pas sûr d’avoir bien compris, tu me corrigeras si je réponds à côté de la plaque :

    Dans mon exemple, j’utilise PostSharp.Laos, qui est un plugin de base facilitant l’interception au dessus de PostSharp.Core (cf. l’architecture de PostSharp)

    C’est en quelque sorte un « plugin AOP », basé sur un post-compiler capable d’analyser et modifier le CIL. Il propose classiquement des moyens de se brancher sur des join points, d’intercepter des affectations, des appels de méthodes, etc. et de modifier ainsi le comportement original des objets.

    Cela signifie que Laos utilise PostSharp.Core pour mettre en place les mécanismes d’interception, et offre ensuite des points d’entrées à ceux qui veulent implémenter leurs aspects lors de ces interceptions.

    On n’a donc pas directement la main sur le CIL lorsqu’on utilise Laos, puisqu’on bénéficie du niveau d’abstraction que proposent la plupart des frameworks d’AOP. Ca signifie que l’assembly, une fois le tissage de l’aspect effectué, ne pourra jamais être aussi « pur » que celui obtenu lorsqu’on implémente INotifyPropertyChanged à la main. Il y a toujours un coût lorsqu’on utilise l’AOP, plus ou moins important selon la nature du tisseur, lié à la plomberie d’interception.

    Voici par exemple un aperçu de ce qui est généré par Laos après avoir interprété mon aspect dans le setter de la propriété Name :

    set
    {
        MethodExecutionEventArgs ~laosEventArgs~2;
        try
        {
            object[] ~arguments~1 = new object[] { value };
            ~laosEventArgs~2 = new MethodExecutionEventArgs(~PostSharp~Laos~Implementation.~targetMethod~3, this, ~arguments~1);
            ~laosEventArgs~2.set_InstanceCredentials(this.GetInstanceCredentials());
            ~PostSharp~Laos~Implementation.WindowsFormsApplication4.FirePropertyChangedAspect~3.OnEntry(~laosEventArgs~2);
            if (~laosEventArgs~2.get_FlowBehavior() != 3)
            {
                this.name = value;
                ~PostSharp~Laos~Implementation.WindowsFormsApplication4.FirePropertyChangedAspect~3.OnSuccess(~laosEventArgs~2);
            }
        }
        catch (Exception ~exception~0)
        {
            ~laosEventArgs~2.set_Exception(~exception~0);
            ~PostSharp~Laos~Implementation.WindowsFormsApplication4.FirePropertyChangedAspect~3.OnException(~laosEventArgs~2);
            switch (~laosEventArgs~2.get_FlowBehavior())
            {
                case 1:
                case 3:
                    return;
            }
            throw;
        }
        finally
        {
            ~PostSharp~Laos~Implementation.WindowsFormsApplication4.FirePropertyChangedAspect~3.OnExit(~laosEventArgs~2);
        }
    }
    

    Pour produire l’implémentation « parfaite » de INotifyPropertyChanged, on pourrait par exemple créer un plugin un plugin très spécifique pour PostSharp.Core.

  4. Matthieu MEZIL said, on novembre 3, 2008 at 12:31

    Pour ma part, ce que j’ai déjà fait c’est utiliser une Factory pour instancier Person qui, au lieu de me faire un new Person va utiliser une classe héritant de RealProxy qui va me retourner un TransparentProxy de Person. Ainsi, côté client c’est transparent, on a l’impression d’utiliser la classe Person et le RealProxy encapsule les appels sur les méthodes de Person, ce qui me permet de rajouter l’appel à FirePropertyChanged (que j’ai passé internal).

  5. Jb Evain said, on novembre 3, 2008 at 1:02

    Moi j’aurais injecté directement l’implémentation avec Cecil (forcément :). Un peu plus compliqué à implémenter, mais zero coût à l’éxécution.

    AspectDNG est mort, mais on peut très bien utiliser un truc super léger, comme Db4oTool (un outil en ligne de commande qui vient avec db4o), pour gérer ses propres instrumentations.

    Parce que bon, les proxy, c’est déjà chiant dynamiquement, alors si en plus on les injecte statiquement, c’est déprimant. Pourtant j’aime bien l’approche `easy` de PostSharp, mais faut croire qu’à force d’avoir les mains dans l’il on y prend goût.

  6. Romain Verdier said, on novembre 3, 2008 at 2:19

    Matthieu> Oui, c’est l’approche à avoir si on choisit le proxying dynamique, que ce soit en utilisant la BCL ou un autre framework comme DynamicProxy. Il subsistera les contraintes citées en début d’article. D’ailleurs si on utilise un RealProxy, une autre de taille viendra s’ajouter à la liste : le fait d’avoir une classe de base. Ca peut sembler assez intrusif par rapport au tissage statique qui permet d’injecter un aspect à n’importe quel type : scélé ou non, dont les membres ne sont pas forcément virtuels, dont on n’a pas le code source, etc.

    Cela dit, dans plein de cas le proxying est utile, voir indispensable. Je n’ai pas cherché à utiliser les Real/Transparent proxys pour INotifyPropertyChanged, mais j’ai lu quelques plaintes relatives aux perfs. Tu en as pensé quoi ?

    Jb> Sure. Comme je le disais dans mon commentaire précédent, la meilleure solution (en terme de perfs tout du moins) consisterait à intervenir directement sur l’assembly généré pour tisser très spécifiquement cet aspect, en produisant le même IL que celui que l’on obtiendrait en implémentant INotifyPropertyChanged à la main. L’intérêt d’utiliser PostSharp pour faire ça était de bénéficier de son intégration au build process (dans l’IDE ou pas) pour rendre la tâche assez transparente. Mais évidemment Cecil semble particulièrement adaptée pour ce genre de manip… Ca doit même être, dans ce cas précis, plus simple que de créer un addin au dessus du core PostSharp. Dès que je trouve un peu de temps je vais essayer de voir ce que ça donne, ne serait-ce que pour mon éveil personnel :)

  7. Matthieu MEZIL said, on novembre 4, 2008 at 9:28

    C’est sûr que les perfs sont dégradées avec l’utilisation du proxy mais dans certaines applications, cela n’est pas problématique.
    C’est vrai que le fait de devoir hériter de MarshallByRef est une vraie contrainte imposée par le RealProxy.
    Cependant, certains clients souhaitent rester dans les classes de la BCL…

  8. Gauthier Segay said, on novembre 9, 2008 at 3:45

    Super article! (tout comme le blog au passage)

    Compte tenu des résultats attendus (plus de string) et si la solution postsharp ne convient pas (perf), je suis assez d’accord avec JB quand à l’utilisation d’une solution vraiment bas niveau car le problème est vraiment sur un périmètre restreint.

    De mon côté j’aurais eu la « flemme » de pousser jusque là (hormis contrainte technique) et j’aurais choisi:
    – proxy dynamique
    – une « ugly » macro C (ah bon pas possible ;))
    – coder avec un langage comme BOO qui permet d’implémenter ses propres macro ce qui devrait permettre de faire ça sans trop de problèmes

    Donc après tout ça on attend l’implémentation finale employant Cecil :)

  9. Romain Verdier said, on novembre 9, 2008 at 7:10

    Matthieu> Yep, et j’ai toujours préféré avoir un peu de liberté. Tous les clients ne se valent pas, alors si j’ai le choix je vais vers celui qui me permet de mettre en place les meilleures solutions possibles.

    Gauthier> Hourra, je tiens enfin une preuve de ton passage sur ce blog :) Et oui, j’ai un draft, voire même deux, qui font intervenir Cecil.

    Alors… Stay tuned !

  10. Hicham said, on novembre 20, 2008 at 1:39

    On on peut trouver le sourcecode de cet essai merci?

  11. Hicham said, on novembre 28, 2008 at 9:20

    Bonjour Romain,

    J’ ai lu votre article “Utiliser l’AOP avec PostSharp pour implémenter INotifyPropertyChanged” et je me suis posé la question, est ce qu’il n’ya pas un moyen d’ executer cet aspect sans que j’utulise l’ attribut (Customize Attribute). C’est à dire, est ce que je peux utiliser l’aspect INotifyPropertyChangedAttribute comme un Plugin, qui peut être integrer à une assemblie.
    Dans votre article, la classe Person peut être compilé et après on lance le Postcompiler Postsharp qui integre le plugin INotifyPropertyChangedAttribute-Aspect dans cette cette classe (assemblie)

    Merci Romain.

    PS: Excusez moi, mon francais n’est pas assez bon, je suis d’allemagne alors vous comprenez.

  12. Hamid MOUTAWAKKIL said, on décembre 6, 2008 at 6:54

    Blog intéressant pour ceux qui s’intéressent à l’AOP et qui retrouvent un moyen de tisser statiquement. Java avait la chance d’avoir AspectJ, et je cherchais la même chose côté DotNet. Je viens de découvrir PostSharp il y a une dizaine de jours, et le post donne un premier aperçu intéressant. Je vais l’étudier d’un peu plus prés, et je pense que je pondrai un futur post dessus.

  13. Simon Mourier said, on avril 21, 2009 at 8:20

    Intéressant article :) Pour ma part, ça m’a totalement convaincu de continuer à écrire OnPropertyChanged avec le texte à la main en dur. Ca a pleins d’avantages, et un seul inconvénient, celui de se tromper en écrivant le nom de la propriété, ce qui peut se vérifier de manière statique avec des outils plus simples et pas intrusifs au moment de l’exécution (règles FXCop, etc…). D’autre part, pour information, les collections du framework 3.0 (orientées WPF) envoient un property changed avec « Item[] » comme nom de propriété. Les chaînes, c’est parfois pratique :)

  14. […] INotifyPropertyChanged avec PostSharp […]

  15. […] recommande également de lire 2 posts d’introduction à l’AOP sur le blog de Romain INotifyPropertyChanged avec PostSharp et INotifyPropertyChanged avec […]

  16. […] recommande également de lire 2 posts d’introduction à l’AOP sur le blog de Romain INotifyPropertyChanged avec PostSharp et INotifyPropertyChanged avec Mono.Cecil Posted in Informatique Tagged […]


Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Image Twitter

Vous commentez à l’aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s

%d blogueurs aiment cette page :