<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Codingly &#187; AOP</title>
	<atom:link href="http://codingly.com/tag/aop/feed/" rel="self" type="application/rss+xml" />
	<link>http://codingly.com</link>
	<description>Par Romain Verdier</description>
	<lastBuildDate>Fri, 27 Jan 2012 13:42:50 +0000</lastBuildDate>
	<language>fr</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='codingly.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://0.gravatar.com/blavatar/62c090a2ec42be744d871177bd874854?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title>Codingly &#187; AOP</title>
		<link>http://codingly.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://codingly.com/osd.xml" title="Codingly" />
	<atom:link rel='hub' href='http://codingly.com/?pushpress=hub'/>
		<item>
		<title>ALT.NET en France : Présentation AOP, c&#8217;est fait.</title>
		<link>http://codingly.com/2009/06/18/alt-net-en-france-presentation-aop-cest-fait/</link>
		<comments>http://codingly.com/2009/06/18/alt-net-en-france-presentation-aop-cest-fait/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 08:09:28 +0000</pubDate>
		<dc:creator>Romain Verdier</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ALT.NET]]></category>
		<category><![CDATA[AOP]]></category>

		<guid isPermaLink="false">http://codingly.com/?p=716</guid>
		<description><![CDATA[Je me demande si 91 slides valent 1000 mots. Vous pourrez essayer de vous faire une idée puisqu&#8217;elles sont disponibles ci-dessous. Je vous conseille de carrément télécharger la présentation si vous êtes intéressés par les animations, puisque certaines (lire : toutes) sont très logiquement tuées par SlideShare. Bon, pour ma part j&#8217;ai trouvé que ça [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=716&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Je me demande si 91 slides valent 1000 mots. Vous pourrez essayer de vous faire une idée puisqu&#8217;elles sont disponibles ci-dessous. Je vous conseille de carrément télécharger la présentation si vous êtes intéressés par les animations, puisque certaines (lire : toutes) sont très logiquement tuées par SlideShare.</p>
<p>Bon, pour ma part j&#8217;ai trouvé que ça s&#8217;était plutôt bien passé considérant que je ne suis pas un speaker &#8211; au mieux un <a href="http://codingly.com/2008/12/29/degage-sale-programmeur/">sale programmeur</a> &#8211; même si j&#8217;imaginais une présentation un peu plus courte. J&#8217;ai parlé pendant 45 min, je crois, puis j&#8217;ai ouvert VS pour la &#8220;démo interactive&#8221; qui s&#8217;est un peu éternisée.</p>
<p>Je tiens encore à remercier <a href="http://fastconnect.fr/">FastConnect</a> pour la salle, l&#8217;organisation, le buffet, etc.<br />
<span id="more-716"></span><br />
<iframe src='http://www.slideshare.net/slideshow/embed_code/1601593' width='720' height='590'></iframe></p>
<p>En ce qui concerne le code des exemples, j&#8217;essaierai d&#8217;éditer ce post ultérieurement.</p>
<p><span class="edittag">Edit</span> Quelques liens en rapport :</p>
<ul>
<li><a href="http://codingly.com/2008/10/29/utiliser-laop-avec-postsharp-pour-implementer-inotifypropertychanged/">INotifyPropertyChanged avec PostSharp</a></li>
<li><a href="http://codingly.com/2008/11/10/introduction-a-monocecil-implementer-inotifypropertychanged/">INotifyPropertyChanged avec Mono.Cecil</a></li>
</ul>
<p><span class="edittag">Edit</span> Le <a href="http://www.altnetfr.org/2009/06/24/altnet-paris-14-compte-rendu-aop/">compte rendu de la soirée</a> est maintenant dispo sur le site ALT.NET Fr. Merci <a href="http://evain.net/blog/">Jb</a>.</p>
<p>N&#8217;hésitez pas à me faire part de vos critiques <em>positives</em>, puisque les autres, constructives ou pas, elles vexent. OK ?</p>
<br />Publié dans Posts Tagged: .NET, ALT.NET, AOP <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/romainverdier.wordpress.com/716/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/romainverdier.wordpress.com/716/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/romainverdier.wordpress.com/716/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=716&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://codingly.com/2009/06/18/alt-net-en-france-presentation-aop-cest-fait/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/cb0ee4bde49708f4be24a02a5d59e52e?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Romain</media:title>
		</media:content>
	</item>
		<item>
		<title>ALT.NET en France : Présentation AOP à Paris le 17 juin</title>
		<link>http://codingly.com/2009/06/02/alt-net-en-france-presentation-aop-a-paris-le-17-juin/</link>
		<comments>http://codingly.com/2009/06/02/alt-net-en-france-presentation-aop-a-paris-le-17-juin/#comments</comments>
		<pubDate>Tue, 02 Jun 2009 21:18:28 +0000</pubDate>
		<dc:creator>Romain Verdier</dc:creator>
				<category><![CDATA[Posts]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[ALT.NET]]></category>
		<category><![CDATA[Annonce]]></category>
		<category><![CDATA[AOP]]></category>

		<guid isPermaLink="false">http://codingly.com/?p=655</guid>
		<description><![CDATA[Avant toute chose : Non, je ne chercherai pas à justifier le rythme misérable de mes publications sur ce blog. Et non, je ne m&#8217;excuserai pas de tricher lamentablement en postant aujourd&#8217;hui quelque chose d&#8217;aussi, heu, bref. Je voulais vous informer, ou plus probablement vous rappeler, que j&#8217;allais commettre une présentation sur l&#8217;AOP, le 17 [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=655&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Avant toute chose : Non, je ne chercherai pas à justifier le rythme misérable de mes publications sur ce blog. Et non, je ne m&#8217;excuserai pas de tricher lamentablement en postant aujourd&#8217;hui quelque chose d&#8217;aussi, heu, bref.</p>
<p>Je voulais vous informer, ou plus probablement vous rappeler, que j&#8217;allais commettre une présentation sur l&#8217;<a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">AOP</a>, le 17 juin, à l&#8217;occasion de la 14ème rencontre ALT.NET Parisienne. Vous trouverez les détails pratiques <a href="http://www.altnetfr.org/2009/05/28/altnet-paris-14-aspect-oriented-programming-aop/">sur notre joli site</a>. Merci à <a href="http://www.fastconnect.fr">FastConnect</a> qui sponsorise l&#8217;évènement.<br />
<span id="more-655"></span><br />
Il va sans dire que je serais plus à l&#8217;aise s&#8217;il n&#8217;y avait que 3 personnes qui jugeaient bon de se déplacer. Pourtant, je vais m&#8217;asseoir sur cette considération et vous inviter à venir nombreux. Plusieurs fois au cours des rencontres précédentes nous avons abordé la question de la programmation orientée aspect lors des discussions libres, et il semblerait donc que le sujet rende curieux, à défaut de préoccuper. Peut-être mérite-t-il que nous lui fassions plus de place le temps d&#8217;une soirée. Je ne sais pas encore exactement quel sera l&#8217;agenda, mais l&#8217;idée est de présenter l&#8217;AOP en tant que concept, de le confronter gentiment aux paradigmes traditionnels, avant de passer plus concrètement à l&#8217;étude des différentes techniques et outils qui existent en .NET.</p>
<p>D&#8217;aucuns pourraient penser que le sujet n&#8217;est pas spécialement ALT.NET.</p>
<p>Ce à quoi je répondrais :</p>
<blockquote><p>Certes, certes&#8230;</p></blockquote>
<p>Enfin. Si on compte uniquement sur Microsoft pour profiter de l&#8217;AOP on risque assez rapidement de se sentir à l&#8217;étroit. Il est possible de faire de l&#8217;AOP sans sortir de la BCL, oui, et il est possible de faire de l&#8217;AOP <em>un peu plus facilement</em> en débordant sur <a href="http://stackoverflow.com/questions/833729/built-in-aop-in-c-is-it-on-the-way/833919#833919">EntLib</a>. Cependant, les solutions les plus matures et les plus riches pour faire de l&#8217;AOP en .NET ne sont pas forcément estampillées Microsoft, ce qui risque de conduire l&#8217;utilisateur averti vers des frameworks plus spécialisés, open-source ou pas. Je veux parler de Castle.DynamicProxy, PostSharp, Linfu, Mono.Cecil, Spring.NET, etc.</p>
<p>Moi, je trouve que ça sent ALT.NET.</p>
<p>Bref, j&#8217;espère que cette rencontre verra naitre un débat et que nous pourrons collecter quelques retours d&#8217;expériences, comparer nos approches, bailler, etc. Encore une fois, toutes les infos sont disponibles sur le site <a href="http://www.altnetfr.org/">altnetfr</a>, à <a href="http://www.altnetfr.org/2009/05/28/altnet-paris-14-aspect-oriented-programming-aop/">cette adresse</a>.</p>
<p>Pensez à <a href="http://www.doodle.com/nd4f57ugtsddqk36">vous inscrire</a>, et n&#8217;hésitez pas à faire passer l&#8217;info.</p>
<br />Publié dans Posts Tagged: .NET, ALT.NET, Annonce, AOP <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/romainverdier.wordpress.com/655/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/romainverdier.wordpress.com/655/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/romainverdier.wordpress.com/655/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=655&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://codingly.com/2009/06/02/alt-net-en-france-presentation-aop-a-paris-le-17-juin/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/cb0ee4bde49708f4be24a02a5d59e52e?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Romain</media:title>
		</media:content>
	</item>
		<item>
		<title>Introduction à Mono.Cecil : Implémenter INotifyPropertyChanged</title>
		<link>http://codingly.com/2008/11/10/introduction-a-monocecil-implementer-inotifypropertychanged/</link>
		<comments>http://codingly.com/2008/11/10/introduction-a-monocecil-implementer-inotifypropertychanged/#comments</comments>
		<pubDate>Mon, 10 Nov 2008 19:20:09 +0000</pubDate>
		<dc:creator>Romain Verdier</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Cecil]]></category>
		<category><![CDATA[CIL]]></category>

		<guid isPermaLink="false">http://romainverdier.wordpress.com/?p=379</guid>
		<description><![CDATA[Vous allez probablement m&#8217;en vouloir à force, mais j&#8217;ai décidé de continuer mes expériences autour d&#8217;INotifyPropertyChanged. Cette fois-ci, en utilisant Mono.Cecil pour faire un peu d&#8217;IL rewriting. Où comment tisser un aspect sans utiliser de framework AOP. Comparée à celle basée sur PostSharp.Laos, cette solution a un inconvénient majeur : elle est plus roots. En [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=379&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Vous allez probablement m&#8217;en vouloir à force, mais j&#8217;ai décidé de continuer mes expériences autour d&#8217;<code>INotifyPropertyChanged</code>. Cette fois-ci, en utilisant <a href="http://www.mono-project.com/Cecil">Mono.Cecil</a> pour faire un peu d&#8217;IL rewriting. Où comment tisser un aspect sans utiliser de framework AOP. Comparée à celle basée sur <a href="http://www.postsharp.org/about/architecture/">PostSharp.Laos</a>, cette solution a un inconvénient majeur : elle est plus roots.</p>
<p>En revanche, aucune autre méthode à ma connaissance ne permet d&#8217;obtenir un tissage aussi fin. Par fin, j&#8217;entends spécifique, propre, non pollué par le code que génèrent les outils d&#8217;AOP classiques pour supporter les mécanismes d&#8217;interception. Donc les performances seront à la clé puisqu&#8217;une fois l&#8217;assembly retravaillé avec Cecil, il ne sera plus possible de faire la différence entre son code IL et celui qui aurait été généré si nous avions implémenté <code>INotifyPropertyChanged</code> à la main. Le développement aura juste été un peu plus cher&#8230;</p>
<p><span id="more-379"></span></p>
<p>Même si la plupart du temps vous préfèrerez sans doute bénéficier de l&#8217;abstraction offerte par les outils d&#8217;AOP classiques, vous trouverez peut-être excitant de jouer avec Cecil pour déveloper des aspects bas niveau en mettant les mains dans le camboui. (Vous pouvez aussi trouver ça naze au point d&#8217;en bailler, hein.)</p>
<p>Bref, ce post va plus ressembler à un article sur Cecil qu&#8217;à un article sur <code>INotifyPropertyChanged</code>. Il y aura probablement deux parties, mais ce n&#8217;est pas une promesse :</p>
<ul>
<li>Part 1 : première version d&#8217;un <em>transformer</em> utilisant Cecil pour implémenter <code>INotifyPropertyChanged</code> (cet article)</li>
<li>Part 2 : version améliorée du <em>transformer</em> lançant l&#8217;évènement de notification uniquement lorsque la valeur affectée à la propriété est différente</li>
</ul>
<p>Eventuellement, je fournirai quelques pistes pour lancer automatiquement ce genre de transformations en post-build dans la seconde partie, si seconde partie il y a.</p>
<p>Mais commençons par le commencement, en prenant pour victime la <code>Person</code> des articles précédents. Le but est de pouvoir écrire :</p>
<p><pre class="brush: csharp;">
	[NotifyPropertyChanged]
	public class Person
	{
		private string name;

		public string Name
		{
			get { return this.name; }
			set { this.name = value; }
		}
	}
</pre></p>
<p>et d&#8217;obtenir au final une classe <code>Person</code> implémentant <code>INotifyPropertyChanged</code> et capable donc de notifier par l&#8217;événement <code>PropertyChanged</code> les changements de ses propriétés.</p>
<p>Cecil permet de charger un assembly, de l&#8217;inspecter et de le modifier éventuellement avant de le sauvegarder, de le copier, etc. L&#8217;idée consiste donc à :</p>
<ol>
<li>Compiler le code ci-dessus normalement, afin d&#8217;obtenir un assembly</li>
<li>Lancer un transformer, qui utilise Cecil afin de charger cet assembly</li>
<li>Modifier l&#8217;assembly, toujours avec Cecil, pour y injecter l&#8217;implémentation d&#8217;<code>INotifyPropertyChanged</code> en manipulant le code CIL.</li>
<li>Sauvegarder l&#8217;assembly modifié</li>
</ol>
<h3>Le transformer</h3>
<p>Dans cette première version, imaginons quelque chose de naïf : un programme en ligne de commande, qui prend en argument le chemin de l&#8217;assembly à instrumenter :</p>
<p><pre class="brush: csharp;">
    private static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine(&quot;Usage: transformer &lt;assembly file&gt;&quot;);
            return;
        }
        new Transformer(path).Run();
    }
</pre></p>
<p>D&#8217;ailleurs, je vais continuer à être naïf en laissant de côté la gestion du logging, des exceptions, etc. Les snippets présentés ici n&#8217;en seront que plus lisibles, à défaut d&#8217;être solides.</p>
<p>Commençons par une vue d&#8217;ensemble des étapes nécessaires à la transformation des assemblies, que nous détaillerons ensuite. (Vous aimez les opcodes ?)</p>
<ol>
<li>Chargement de l&#8217;assembly à instrumenter</li>
<li>Identification des types à modifier : on recherche ceux qui sont taggés par notre attribut <code>[NotifyPropertyChanged]</code></li>
<li>Transformation de chacun de ces types pour leur faire supporter <code>INotifyPropertyChanged</code></li>
<li>Sauvegarde de l&#8217;assembly modifié</li>
</ol>
<p>Le schéma ci-dessous met en avant les différentes étapes de la transformation :<br />
<img src="http://romainverdier.files.wordpress.com/2008/11/transformer.jpg?w=720" alt="transformer" title="transformer"   class="alignnone size-full wp-image-393" /></p>
<ul>
<li>[#1] Implémentation proprement dite de l&#8217;interface <code>INotifyPropertyChanged</code></li>
<li>[#2] Ajout d&#8217;une fonction &#8220;helper&#8221; pour le déclenchement de l&#8217;évènement <code>PropertyChanged</code></li>
<li>[#3] Instrumentation des setters, en utilisant la méthode précédente pour lancer les notifications</li>
</ul>
<p>Voyons comment implémenter ça. Rappelez-vous, tout commence par :</p>
<p><pre class="brush: csharp;">
	new Transformer(path).Run();
</pre></p>
<p>Le constructeur (qui en fait sans doute un peu trop) :</p>
<p><pre class="brush: csharp;">
public Transformer(string assemblyPath)
{
    this.assemblyPath = assemblyPath;
    this.assembly = AssemblyFactory.GetAssembly(assemblyPath);
    this.assembly.MainModule.LoadSymbols();
    this.assembly.MainModule.FullLoad();
}
</pre></p>
<p>Assez trivial : on laisse tout le travail à Cecil. Son <code>AssemblyFactory</code> nous permet de récupérer la définition de l&#8217;assembly à modifier. Afin de conserver les sequence points et permettre de debuguer l&#8217;assembly modifiée, on charge les infos de debug. L&#8217;appel à la méthode <code>FullLoad</code> permet de charger en mémoire toute la représentation du module principal, afin de pouvoir la modifier par la suite.</p>
<p><strong>Petite parenthèse introductive</strong> : Plusieurs fois, dans les méthodes que je vais présenter par la suite, on aura besoin de récupérer une <code>TypeReference</code>, à partir d&#8217;un <code>Type</code>. Lorsque ce sera le cas, on utilisera une des deux méthodes suivantes :</p>
<p><pre class="brush: csharp;">
private TypeReference GetReference(Type type)
{
    TypeReference typeReference = this.assembly.MainModule.TypeReferences[type.FullName]
                                  ?? this.assembly.MainModule.Import(type);
    return typeReference;
}

private TypeReference GetReference&lt;T&gt;()
{
    return GetReference(typeof (T));
}
</pre></p>
<p>Leur fonctionnement est simple : si on a déjà chargé le type dans le module principal de l&#8217;assembly en cours de modification, on retourne directement la <code>TypeReference</code> correspondante, sinon on importe le type pour créer la référence. Car Cecil fait bien la distinction (qui existe au niveau CIL) entre les <em>définitions</em> et les <em>références</em> pour les différents éléments du code (types, fields, methods, etc.)</p>
<p>Lorsqu&#8217;on veut modifier une méthode, par exemple, on va travailler sur sa définition. Mais si on désire simplement l&#8217;appeler, il nous faut juste sa référence. Ce modèle, plus juste que celui proposé par l&#8217;API de réflexion de la BCL peut sembler troublant au premier abord, mais s&#8217;avère au final plus puissant puisqu&#8217;il colle directement avec les <a href="http://www.ecma-international.org/publications/standards/Ecma-335.htm">spécifications du langage intermédiaire</a>. <strong>Fin de la parenthèse</strong>.</p>
<p>Bien, on a de quoi commencer l&#8217;autopsie du <code>Transformer</code>, dont le nom est plus que douteux&#8230; La méthode <code>Run</code> semble être un point de départ judicieux pour notre analyse :</p>
<p><pre class="brush: csharp;">
public void Run()
{
    foreach (TypeDefinition type in GetTypesToWeave())
    {
        MethodReference firePropertyChangedMethod = WeaveINotifyPropertyChanged(type);
        WeaveEventTriggers(type, firePropertyChangedMethod);
    }
    this.assembly.MainModule.SaveSymbols();
    AssemblyFactory.SaveAssembly(this.assembly, this.assemblyPath);
}
</pre></p>
<p>La méthode <code>GetTypesToWeave</code> permet de retourner tous les types à modifier, c&#8217;est à dire ceux qui sont décorés par notre attribut <code>[NotifyPropertyChanged]</code>. Ce dernier devra donc être défini dans un assembly quelconque, référencé à la fois par le code client &#8211; celui qui contient les types à instrumenter &#8211; et par notre transformer. Chacun des types est ensuite transformé par les appels successifs aux méthodes <code>WeaveINotifyPropertyChanged</code> et <code>WeaveEventTriggers</code>.</p>
<p>La sauvegarde des modifications s&#8217;effectue elle aussi en plusieurs étapes :</p>
<ul>
<li>On indique qu&#8217;on souhaite conserver les symboles de debugging</li>
<li>On sauvegarde notre travail en remplaçant l&#8217;assembly original par celui que l&#8217;on vient d&#8217;instrumenter.
</ul>
<p>Jusqu&#8217;ici, tout va bien, comme qui dirait. Mais creusons un peu plus. Tout d&#8217;abord, le code de la méthode <code>GetTypesToWeave</code> qui nous permet de retourner les types de l&#8217;assembly à modifier. Il s&#8217;agit essentiellement d&#8217;alléger la méthode <code>Run</code> en évitant les imbrications et en isolant la logique de recherche dans une autre méthode :</p>
<p><pre class="brush: csharp;">
private IEnumerable&lt;TypeDefinition&gt; GetTypesToWeave()
{
    string attributeFullName = typeof (NotifyPropertyChangedAttribute).FullName;
    foreach (TypeDefinition type in this.assembly.MainModule.Types)
    {
        foreach (CustomAttribute attribute in type.CustomAttributes)
        {
            if (attribute.Constructor.DeclaringType.FullName == attributeFullName)
            {
                yield return type;
                break;
            }
        }
    }
}
</pre></p>
<p>Notez qu&#8217;on utilise pour cela un bloc itérateur. Le reste pourrait se passer de commentaire : on retourne les types marqués par l&#8217;attribut <code>[NotifyPropertyChanged]</code> en basant la recherche sur le fullname des attributs.</p>
<p>Passons maintenant à la modification des types. Rappelez-vous, il y a deux étapes principales :</p>
<ul>
<li>Implémentation de <code>INotifyPropertyChanged</code> (méthode <code>WeaveINotifyPropertyChanged</code>)</li>
<li>Instrumentation des setters (méthode <code>WeaveEventTriggers</code>)</li>
</ul>
<h3>Implémentation de <code>INotifyPropertyChanged</code></h3>
<p>L&#8217;implémentation proprement dite de <code>INotifyPropertyChanged</code> est une tâche que l&#8217;on peut aisément redécomposer. Cette redécomposition est d&#8217;ailleurs assez visible dans la méthode <code>WeaveINotifyPropertyChanged</code> que je propose :</p>
<p><pre class="brush: csharp;">
private MethodReference WeaveINotifyPropertyChanged(TypeDefinition type)
{
    TypeReference iNotifyPropertyChangedType = GetReference&lt;INotifyPropertyChanged&gt;();
    type.Interfaces.Add(iNotifyPropertyChangedType);
    FieldReference eventField = WeaveEvent(type);
    
    return WeaveEventFiringHelper(eventField, type);
}
</pre></p>
<p>La première étape consiste à récupérer une <code>TypeReference</code> correspondant à cette interface pour l&#8217;ajouter à la liste des interfaces du type que l&#8217;on modifie. Simple, mais insuffisant : il faut également <em>implémenter</em> l&#8217;interface. Dans le cas d&#8217;<code>INotifyPropertyChanged</code>, on doit ajouter un évènement public <code>PropertyChanged</code> au type. C&#8217;est la méthode <code>WeaveEvent</code> qui s&#8217;en chargera. (Etape [#1] du schéma)</p>
<p>La méthode <code>WeaveEventFiringHelper</code>, quant à elle, permet d&#8217;ajouter une méthode <code>FirePropertyChanged</code> au type. Quelque chose équivalent à :</p>
<p><pre class="brush: csharp;">
    private void FirePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
</pre></p>
<p>C&#8217;est l&#8217;étape [#2] de la transformation, également signalée dans le schéma précédent. La référence vers cette méthode est retournée pour permettre son appel plus tard à partir des setters des propriétés. </p>
<p>Dès que vous aurez terminé de bailler, nous détaillerons la première étape qui consiste à ajouter l&#8217;évènement <code>PropertyChanged</code> au type. Soit la méthode <code>WeaveEvent</code> :</p>
<p><pre class="brush: csharp;">
private FieldReference WeaveEvent(TypeDefinition type)
{
    TypeReference eventHandlerTypeRef = GetReference&lt;PropertyChangedEventHandler&gt;();
    MethodReference combineMethodRef = this.assembly.MainModule.Import(combineMethodBase);
    MethodReference removeMethodRef = this.assembly.MainModule.Import(removeMethodBase);
    const string eventName = &quot;PropertyChanged&quot;;

    FieldDefinition eventField = new FieldDefinition(&quot;PropertyChanged&quot;,
                                                     eventHandlerTypeRef,
                                                     FieldAttributes.Private);
    type.Fields.Add(eventField);

    EventDefinition eventDefinition = new EventDefinition(eventName,
                                                          eventHandlerTypeRef,
                                                          0);

    eventDefinition.AddMethod = CreateEventMethod(string.Format(&quot;add_{0}&quot;, eventName),
                                                  combineMethodRef,
                                                  eventField);

    eventDefinition.RemoveMethod = CreateEventMethod(string.Format(&quot;remove_{0}&quot;, eventName),
                                                     removeMethodRef,
                                                     eventField);

    type.Methods.Add(eventDefinition.AddMethod);
    type.Methods.Add(eventDefinition.RemoveMethod);
    type.Events.Add(eventDefinition);

    return eventField;
}
</pre></p>
<p>Le layout n&#8217;est pas terrible, j&#8217;espère que vous ne m&#8217;en tiendrez pas rigueur.</p>
<p>Petit rappel : Lorsqu&#8217;on est au niveau CIL, on a une bonne visibilité sur ce qu&#8217;un langage de plus haut niveau comme C# peut nous cacher. Les évènements constituent un bon exemple. En C#, la déclaration d&#8217;un event est simple :</p>
<p><pre class="brush: csharp;">
public event PropertyChangedEventHandler PropertyChanged;
</pre></p>
<p>Au niveau IL, le compilateur C# va en fait générer un champ privé encapsulé par une propriété un peu spéciale de type <code>event</code> qui possède les méthodes &#8220;<code>add</code>&#8221; et &#8220;<code>remove</code>&#8220;. Note : même si ce n&#8217;est pas souvent utilisé, sachez qu&#8217;à partir de C# il est possible de <a href="http://msdn.microsoft.com/en-us/library/aa664650(VS.71).aspx">faire apparaitre ces méthodes</a> pour éventuellement intercepter les abonnements / désabonnements à l&#8217;évènement. </p>
<p>Pour ajouter l&#8217;évènement <code>PropertyChanged</code> à notre type, nous avons donc :</p>
<ul>
<li>Ajouté un champ d&#8217;instance privé de type <code>NotifyPropertyChangedEventHandler</code></li>
<li>Ajouté l&#8217;évènement (pseudo propriété), qui utilise deux méthodes : <code>add_PropertyChanged</code> et <code>remove_PropertyChanged</code></li>
<li>Créé les deux méthodes précédentes, qui utilisent respectivement les méthodes statiques <code>Delegate.Combine</code> et <code>Delegate.Remove</code> pour ajouter et supprimer les handlers au champ d&#8217;instance</li>
</ul>
<p>Puisque l&#8217;IL n&#8217;est pas très lisible, voici la traduction C# de ces deux méthodes :</p>
<p><pre class="brush: csharp;">
public void add_PropertyChanged(PropertyChangedEventHandler value)
{
    this.PropertyChanged = 
		(PropertyChangedEventHandler) Delegate.Combine(this.PropertyChanged, value);
}

public void remove_PropertyChanged(PropertyChangedEventHandler value)
{
    this.PropertyChanged = 
		(PropertyChangedEventHandler) Delegate.Remove(this.PropertyChanged, value);
}
</pre></p>
<p>Dans notre exemple précédent, on délègue la création de ces deux méthodes à <code>CreateEventMethod</code> dont voici la définition :</p>
<p><pre class="brush: csharp;">
private MethodDefinition CreateEventMethod(string methodName,
                                           MethodReference delegateMethodReference,
                                           FieldReference eventField)
{
    const MethodAttributes attributes = MethodAttributes.Public |
                                        MethodAttributes.HideBySig |
                                        MethodAttributes.Final |
                                        MethodAttributes.SpecialName |
                                        MethodAttributes.NewSlot |
                                        MethodAttributes.Virtual;

    TypeReference eventHandlerTypeRef = GetReference&lt;PropertyChangedEventHandler&gt;();
    TypeReference voidType = GetReference(typeof (void));

    MethodDefinition methodDef = new MethodDefinition(methodName, attributes, voidType);


    methodDef.Parameters.Add(new ParameterDefinition(eventHandlerTypeRef));
    CilWorker cilWorker = methodDef.Body.CilWorker;
    cilWorker.Emit(OpCodes.Ldarg_0);
    cilWorker.Emit(OpCodes.Ldarg_0);
    cilWorker.Emit(OpCodes.Ldfld, eventField);
    cilWorker.Emit(OpCodes.Ldarg_1);
    cilWorker.Emit(OpCodes.Call, delegateMethodReference);
    cilWorker.Emit(OpCodes.Castclass, eventHandlerTypeRef);
    cilWorker.Emit(OpCodes.Stfld, eventField);
    cilWorker.Emit(OpCodes.Ret);
    return methodDef;
}
</pre></p>
<p>Notez l&#8217;API intuitive que propose Cecil pour ce genre de choses : on instancie une définition de l&#8217;élément que l&#8217;on cherche à ajouter au type (<code>FieldDefinition</code>, <code>EventDefinition</code>, <code>MethodDefinition</code>, etc.), on la configure si besoin, et on l&#8217;ajoute à la définition parente. Pour ce qui est de l&#8217;émission d&#8217;IL pour le corps des méthodes, il suffit d&#8217;obtenir un <code>CilWorker</code> et de créer séquentiellement les instructions en utilisant les codes d&#8217;opérations du langage intermédiaire.</p>
<p>Voilà pour la création de l&#8217;évènement <code>PropertyChanged</code>. Notre type est à présent une implémentation valide de l&#8217;interface <code>INotifyPropertyChanged</code>, puisque qu&#8217;il respecte le contrat imposé par cette dernière : exposer un évènement <code>PropertyChanged</code>, de type <code>PropertyChangedEventHandler</code>. (Etape [#1] du schéma)</p>
<p>Il ne reste plus qu&#8217;à déclencher cet évènement, et pour cela, il est habituellement préférable de prévoir une méthode spécifique qui sera appellée à partir des setters (Etape [#2]). Nous l&#8217;appellons ici <code>FireNotifyPropertyChanged</code> et c&#8217;est la méthode <code>WeaveEventFiringHelper</code> de notre transformer qui se charge de l&#8217;ajouter au type instrumenté :</p>
<p><pre class="brush: csharp;">
private MethodReference WeaveEventFiringHelper(FieldReference eventField, TypeDefinition type)
{
    MethodReference invokeMethodRef = this.assembly.MainModule.Import(invokeMethodBase);
    MethodReference argsCtorMethodRef = this.assembly.MainModule.Import(constructorMethodBase);
    TypeReference stringTypeRef = GetReference&lt;string&gt;();
    TypeReference voidTypeRef = GetReference(typeof (void));

    MethodDefinition firePropertyChanged = 
        new MethodDefinition(&quot;FirePropertyChanged&quot;, MethodAttributes.Private, type);
    firePropertyChanged.ReturnType = new MethodReturnType(voidTypeRef);
    ParameterDefinition propertyNameParameter =
        new ParameterDefinition(&quot;propertyName&quot;, 0, ParameterAttributes.None, stringTypeRef);
    firePropertyChanged.Parameters.Add(propertyNameParameter);

    CilWorker cilWorker = firePropertyChanged.Body.CilWorker;

    Instruction returnInstruction = cilWorker.Create(OpCodes.Ret);
    cilWorker.Emit(OpCodes.Ldarg_0);
    cilWorker.Emit(OpCodes.Ldfld, eventField);
    cilWorker.Emit(OpCodes.Brfalse_S, returnInstruction);
    cilWorker.Emit(OpCodes.Ldarg_0);
    cilWorker.Emit(OpCodes.Ldfld, eventField);
    cilWorker.Emit(OpCodes.Ldarg_0);
    cilWorker.Emit(OpCodes.Ldarg_1);
    cilWorker.Emit(OpCodes.Newobj, argsCtorMethodRef);
    cilWorker.Emit(OpCodes.Callvirt, invokeMethodRef);
    cilWorker.Append(returnInstruction);
    type.Methods.Add(firePropertyChanged);

    return firePropertyChanged;
}
</pre></p>
<p>La <code>FieldReference</code> récupérée en argument est celle retournée par la méthode précédente, <code>WeaveEvent</code>, qui a ajouté l&#8217;évènement au type. Elle représente d&#8217;ailleurs le champ de l&#8217;évènement, qui doit être utilisé pour l&#8217;invocation. La traduction C# de la méthode générée dynamiquement correspond à :</p>
<p><pre class="brush: csharp;">
    private void FirePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
</pre></p>
<p>C&#8217;est complètement équivalent au code de l&#8217;étape [#2] dans le premier schéma.</p>
<h3>Instrumentation des setters</h3>
<p>A ce stade, pour résumer, nous avons :</p>
<ul>
<li>[#1] Implémenté <code>INotifyPropertyChanged</code> en ajoutant un évènement <code>PropertyChanged</code> au type</li>
<li>[#2] Créé une méthode &#8220;helper&#8221; pour déclencher l&#8217;évènement</li>
</ul>
<p>Très logiquement, il ne reste plus qu&#8217;à appeller cette dernière à partir de tous les setters des propriétés du type que l&#8217;on modifie (étape [#3]). Cette fois-ci, on ne va pas créer un nouveau champ, un nouvel évènement ou une nouvelle méthode, on va <em>modifier</em> le code CIL du corps de méthodes existantes. Car un setter de propriété est une méthode traditionnelle.</p>
<p>Examinons la méthode <code>WeaveEventTriggers</code> du transformer, qui est responsable de cette tache :</p>
<p><pre class="brush: csharp;">
private static void WeaveEventTriggers(TypeDefinition type, MethodReference firePropertyChanged)
{
    foreach (PropertyDefinition property in type.Properties)
    {
        MethodDefinition method = property.SetMethod;
        if (method == null)
        {
            continue;
        }

        Instruction retInstruction = method.Body.Instructions[method.Body.Instructions.Count - 1];

        CilWorker cilWorker = method.Body.CilWorker;
        cilWorker.InsertBefore(retInstruction, cilWorker.Create(OpCodes.Ldarg_0));
        cilWorker.InsertBefore(retInstruction, cilWorker.Create(OpCodes.Ldstr, property.Name));
        cilWorker.InsertBefore(retInstruction, cilWorker.Create(OpCodes.Call, firePropertyChanged));
    }
}
</pre></p>
<p>Encore une fois, l&#8217;implémentation est naïve et nous verrons s&#8217;il est possible de l&#8217;améliorer plus tard. La logique est simple :</p>
<ul>
<li>On itère sur <em>toutes</em> les propriétés du type. (Pas très malin&#8230;)</li>
<li>On récupère la définition de la méthode du setter.</li>
<li>On récupère une référence vers la dernière instruction du corps du setter</li>
<li>On injecte le code CIL correspondant à l&#8217;appel de la méthode <code>FirePropertyChanged</code> avant la dernière instruction pour que la notification soit lançée à la fin de chaque modification.</li>
</ul>
<p>En gros, si le setter en C# est du genre :</p>
<p><pre class="brush: csharp;">
	public void set_Name(string value)
	{
		this.name = value;
	}
</pre></p>
<p>On le transforme en :</p>
<p><pre class="brush: csharp;">
	public void set_Name(string value)
	{
		this.name = value;
		FirePropertyChanged(&quot;Name&quot;);
	}
</pre></p>
<p>Note : <code>combineMethodBase</code>, <code>invokeMethodBase</code>, <code>constructorMethodBase</code> et <code>removeMethodBase</code>, référencés dans les méthodes du <code>Transformer</code>, sont des membres statiques initialisés de la façon suivante :</p>
<p><pre class="brush: csharp;">
private static readonly MethodBase combineMethodBase;
private static readonly MethodBase invokeMethodBase;
private static readonly MethodBase constructorMethodBase;
private static readonly MethodBase removeMethodBase;

static Transformer()
{
    var type = typeof (Delegate);
    combineMethodBase = type.GetMethod(&quot;Combine&quot;, new[] {type, type});
    removeMethodBase = type.GetMethod(&quot;Remove&quot;);
    type = typeof (PropertyChangedEventHandler);
    invokeMethodBase = type.GetMethod(&quot;Invoke&quot;);
    type = typeof (PropertyChangedEventArgs);
    constructorMethodBase = type.GetConstructor(new[] {typeof (string)});
}
</pre></p>
<p>Les <code>MethodBase</code> sont des types de l&#8217;API d&#8217;introspection de <code>System.Reflection</code>. Il est facile de récupérer les <code>MethodReference</code> correspondantes (API Cecil) en utilisant la méthode <code>Import</code> d&#8217;une <code>ModuleDefinition</code>.</p>
<h3>Conclusion intermédiaire</h3>
<p>Voilà une première version du &#8220;transformer&#8221; capable d&#8217;instrumenter un assembly existant pour faire implémenter <code>INotifyPropertyChanged</code> à certains de ses types automatiquement. Les améliorations possibles sont nombreuses, et je ne vais en citer que quelques unes :</p>
<ul>
<li>Meilleure utilisation de Cecil : après tout, je découvre et suis donc loin d&#8217;être un expert</li>
<li>Meilleure intégration au process de build, en utilisant une custom task MSBuild par exemple</li>
<li>Version complète de l&#8217;instrumentation : l&#8217;évènement <code>PropertyChanged</code> ne doit être déclenché que lorsque la valeur affectée à la propriété est différente</li>
<li>Gestion des exceptions et du logging (évidemment)</li>
<li>Etc.</li>
</ul>
<p>Cecil est un outil extrêmement puissant et facile d&#8217;utilisation. Pour info, la mise en place de cette version du <code>Transformer</code> n&#8217;a pas dû me prendre 2 heures, alors que <strike>la femme de ma vie me parlait</strike> je faisais autre chose en même temps, et que je découvrais complètement l&#8217;API non documentée. Il est préférable par contre de connaître le CIL, même si ILdasm ou Reflector peuvent vous assister efficacement. Enfin, et presque paradoxalement, la prise en main de Cecil vous paraitra déroutante si vous avez l&#8217;habitude d&#8217;utiliser <code>System.Reflection</code> et <code>System.Reflection.Emit</code>.</p>
<p>Au final ma première expérience est définitivement positive, et je n&#8217;hésiterai pas dans le futur à recourir à Cecil au besoin. Je pense d&#8217;ailleurs qu&#8217;en collaboration avec une couche facilitant son utilisation dans un context post-build, on aurait quelque chose d&#8217;assez terrible, probablement plus simple à utiliser que PostSharp.Core par exemple pour des tâches bien spécifiques.</p>
<p>Notez tout de même que l&#8217;exemple d&#8217;utilisation de Cecil présenté ici est un peu tordu, et qu&#8217;il y a bien d&#8217;autres (meilleurs) <a href="http://www.mono-project.com/Cecil#Projects">moyens d&#8217;exploiter ses capacités</a>.</p>
<br />Publié dans Articles Tagged: .NET, AOP, C#, Cecil, CIL <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/romainverdier.wordpress.com/379/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/romainverdier.wordpress.com/379/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/romainverdier.wordpress.com/379/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=379&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://codingly.com/2008/11/10/introduction-a-monocecil-implementer-inotifypropertychanged/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/cb0ee4bde49708f4be24a02a5d59e52e?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Romain</media:title>
		</media:content>

		<media:content url="http://romainverdier.files.wordpress.com/2008/11/transformer.jpg" medium="image">
			<media:title type="html">transformer</media:title>
		</media:content>
	</item>
		<item>
		<title>Utiliser l&#8217;AOP avec PostSharp pour implémenter INotifyPropertyChanged</title>
		<link>http://codingly.com/2008/10/29/utiliser-laop-avec-postsharp-pour-implementer-inotifypropertychanged/</link>
		<comments>http://codingly.com/2008/10/29/utiliser-laop-avec-postsharp-pour-implementer-inotifypropertychanged/#comments</comments>
		<pubDate>Wed, 29 Oct 2008 23:09:58 +0000</pubDate>
		<dc:creator>Romain Verdier</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[.NET]]></category>
		<category><![CDATA[AOP]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[PostSharp]]></category>
		<category><![CDATA[Tips]]></category>

		<guid isPermaLink="false">http://romainverdier.wordpress.com/?p=319</guid>
		<description><![CDATA[J&#8217;ai ce billet dans mes drafts depuis un moment, et il serait peut-être bon de l&#8217;en sortir avant qu&#8217;il ne soit trop tard&#8230; Dans un post récent, je parlais de l&#8217;interface INotifyPropertyChanged, en donnant un exemple d&#8217;implémentation basée sur les lambda expressions et les expression trees qui permettait d&#8217;obtenir une solution fortement typée. L&#8217;AOP peut [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=319&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>J&#8217;ai ce billet dans mes drafts depuis un moment, et il serait peut-être bon de l&#8217;en sortir avant qu&#8217;il ne soit trop tard&#8230; Dans un <a href="http://codingly.com/2008/09/30/inotifypropertychanged-sans-les-strings-ca-vous-dit/">post récent</a>, je parlais de l&#8217;interface <code>INotifyPropertyChanged</code>, en donnant un exemple d&#8217;implémentation basée sur les lambda expressions et les expression trees qui permettait d&#8217;obtenir une solution fortement typée.</p>
<p>L&#8217;AOP peut également être une solution, et peut-être même le meilleur compromis pour peu que l&#8217;on en tire partie correctement. Suite à un <a href="http://codingly.com/2008/09/30/inotifypropertychanged-sans-les-strings-ca-vous-dit/#comment-139">commentaire de Jb</a>, j&#8217;ai essayé de voir ce que pouvait donner l&#8217;utilisation d&#8217;un tisseur statique comme <a href="http://www.postsharp.org/">PostSharp</a> dans ce cas précis.<br />
<span id="more-319"></span></p>
<p>J&#8217;avais déjà fait auparavant quelques tests satisfaisants avec <a href="http://www.castleproject.org/dynamicproxy/index.html">DynamicProxy</a> de Castle, qui est un, heu, tisseur dynamique. Les contraintes m&#8217;avaient semblées être assez fortes (toujours pour résoudre la problématique de <code>INotifyPropertyChanged</code>) :</p>
<ul>
<li>Nécessité de créer un proxy pour chaque instance d&#8217;objet bindé</li>
<li>Interception uniquement possible sur les propriétés virtuelles</li>
<li>Performances moyennes dues au caractère dynamique des interceptions</li>
</ul>
<p>Cela dit, en terme de concision, c&#8217;est probablement la méthode la plus efficace puisqu&#8217;en créant un <code>StandardInterceptor</code> d&#8217;une dizaine de lignes on pouvait s&#8217;en sortir.</p>
<p>Revenons à PostSharp. J&#8217;ai été impressionné, vraiment, par cet outil. Sans passer trop de temps à expliquer son fonctionnement, puisque <a href="http://gael.fraiteur.net/">Gael Fraiteur</a> l&#8217;a <a href="http://www.postsharp.org/about/">déjà fait</a>, 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&#8217;auteur sur le site, elles sont assez complètes. Dans le contexte de notre problématique (oui, les notifications m&#8217;obsèdent) les avantages du tissage en post-compilation sont les suivants :</p>
<ul>
<li>Pas de proxy à créer au runtime <em>pour chaque instance</em></li>
<li>Les types sur lesquels on désire tisser l&#8217;aspect &#8220;notification-des-changements&#8221; n&#8217;ont pas besoin de respecter une quelconque convention</li>
<li>Une partie du travail peut être faite durant le build process, ce qui améliore les performances au runtime.</li>
</ul>
<p>Illustrons par le code.</p>
<p>Voici mon POCO (je vous conseille d&#8217;imaginer le même avec 15 propriétés) :</p>
<p><pre class="brush: csharp;">
    public class Person
    {
        private string name;

        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
    }
</pre></p>
<p>Voici ce que je cherche à obtenir, fonctionnellement, sans avoir à écrire le code de plomberie :</p>
<p><pre class="brush: csharp;">
    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(&quot;Name&quot;);
                }
            }
        }

        private void FirePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
</pre></p>
<p>En fait, voici ce que je cherche <em>réellement</em> à obtenir :</p>
<p><pre class="brush: csharp;">
    [NotifyPropertyChanged]
    public class Person
    {
        private string name;

        public string Name
        {
            get { return this.name; }
            set { this.name = value; }
        }
    }
</pre></p>
<p>La différence avec la première version est subtile : un attribut <code>NotifyPropertyChanged</code>. Je veux que tout soit transparent au niveau de mon code client. Que des instances de <code>Person</code> puissent être créées, ajoutées à des sources de données, modifiées comme n&#8217;importe quelles autres instances, etc.</p>
<p>Donc, exit le proxying, exit l&#8217;interception dynamique : ceux qui y sont habitués ont déjà compris que ce n&#8217;était pas possible. C&#8217;est là que réside la force d&#8217;un tisseur comme PostSharp (ou <a href="http://aspectdng.tigris.org/">AspectDNG</a>) : ils rendent <em>possible</em> ce genre de choses, grâce à l&#8217;approche potentiellement troublante du tissage statique.</p>
<p>Pour la suite de l&#8217;article, je vais simplement vous montrer une solution, et la commenter. Il ne s&#8217;agit pas d&#8217;un tutorial &#8211; mais plutôt d&#8217;une pseudo-étude de cas ayant pour but de donner un bon aperçu des capacités de PostSharp. Et accessoirement d&#8217;apaiser mon obsession.</p>
<h3>Le principe</h3>
<p>Utiliser PostSharp est un peu moins intuitif qu&#8217;utiliser DynamicProxy. Je vais tenter de vous exposer le principe de base de la solution.</p>
<p>Que faut-il pour passer d&#8217;un POCO à un POCO qui supporte <code>INotifyPropertyChanged</code> :</p>
<ol>
<li>Implémenter l&#8217;interface du même nom : <code>INotifyPropertyChanged</code>, et donc, fournir un event public ayant la signature suivante : <code>public event PropertyChangedEventHandler PropertyChanged;</code></li>
<li>Intercepter les accès en écriture aux propriétés, pour déclencher cet évènement <code>PropertyChanged</code>. Dans l&#8217;exemple donné plus haut, on va même jusqu&#8217;à tester s&#8217;il est nécessaire de déclencher l&#8217;évènement en comparant la nouvelle valeur à l&#8217;existante.</li>
</ol>
<p>Ce sont deux choses distinctes, bien qu&#8217;on ait souvent tendance à les associer, ne serait-ce que parce que c&#8217;est logique. Mais pour bien comprendre comment utiliser l&#8217;AOP, et plus particulièrement comment concevoir l&#8217;aspect que l&#8217;on va vouloir tisser grâce à PostSharp, c&#8217;est important. Car nous allons en fait utiliser un aspect <strong>composite</strong>, formé de plusieurs sous-aspects ayant chacun un but précis.</p>
<p><strong>Interception des affectations des champs privés</strong></p>
<p>Ce sous-aspect, de type <code>OnFieldAccessAspect</code>, a un rôle facultatif : il va vérifier que la valeur que l&#8217;on cherche à affecter à un champ est différente de la valeur existante. Cela permettra de savoir si l&#8217;on doit ou non déclencher l&#8217;évènement <code>PropertyChanged</code>.</p>
<p><strong>Implémentation de INotifyPropertyChanged</strong></p>
<p>Le but de ce sous-aspect et d&#8217;implémenter <code>INotifyPropertyChanged</code>. PostSharp propose un <code>CompositionAspect</code> qui, lorsqu&#8217;il est appliqué à un type, se charge de lui faire implémenter une interface donnée en utilisant la <em>composition</em> et la <em>délégation</em>.</p>
<p><img src="http://romainverdier.files.wordpress.com/2008/10/postsharp.png?w=720" alt="" title="postsharp"   class="aligncenter size-full wp-image-330" /></p>
<p>Le POCO implémentera bien l&#8217;interface <code>INotifyPropertyChanged</code>, mais les abonnements/désabonnements à son évènement <code>PropertyChanged</code> seront interceptés et relayés à un composant (<code>InnerNotifier</code> ci-dessus) implémentant également <code>INotifyPropertyChanged</code>. Quelque chose du genre :</p>
<p><pre class="brush: csharp;">
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { this.innerNotifier.PropertyChanged += value; }
        remove { this.innerNotifier.PropertyChanged -= value; }
    }
</pre></p>
<p>Le <code>CompositionAspect</code> a donc deux tâches : </p>
<ol>
<li>Définir quelle interface le POCO doit implémenter. Une petite idée ?</li>
<li>Fournir un composant qui se chargera de la réelle logique d&#8217;implémentation de l&#8217;interface, et qui, nous le verrons plus tard, offrira également un moyen de déclencher l&#8217;évènement <code>PropertyChanged</code> (méthode <code>FirePropertyChanged</code> ci-dessus)</li>
</ol>
<p><strong>Interception des appels aux setters des propriétés</strong></p>
<p>Ce sous-aspect a pour mission de déclencher l&#8217;évènement <code>PropertyChanged</code> lorsqu&#8217;un setter est appelé et que le backing field a bien été modifié. Il doit donc <em>interagir</em>, d&#8217;une manière ou d&#8217;une autre, avec l&#8217;aspect précédent pour déclencher l&#8217;évènement.</p>
<p>Il est de type <code>OnMethodBoundaryAspect</code> selon l&#8217;API PostSharp.</p>
<p><strong>Composition de l&#8217;aspect composite</strong></p>
<p>Enfin, il faut l&#8217;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&#8217;aspects composites : il suffit de créer un <code>CompoundAspect</code>.</p>
<h3>La solution</h3>
<p>Si l&#8217;approche down-top correspondait plutôt bien à l&#8217;exposition du principe de fonctionnement, je crois qu&#8217;il est plus clair ici de partir du plus haut niveau pour découvrir l&#8217;implémentation de la solution proposée. </p>
<p>Et le point de départ, c&#8217;est l&#8217;attribut <code>[NotifyPropertyChanged]</code> dont on souhaitait décorer les POCO à tisser. En voici le code :</p>
<p><pre class="brush: csharp;">
[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());
        }
    }
}
</pre></p>
<p>C&#8217;est l&#8217;aspect composé lui-même, le <code>CompoundAspect</code>. Analysons-le un peu plus en détails :</p>
<ul>
<li>Il est lui-même décoré par des attributs définissant les cibles possibles de l&#8217;aspect. Ici, on vise les types. PostSharp permet aussi l&#8217;application d&#8217;aspects par <a href="http://doc.postsharp.org/1.0/UserGuide/Laos/Multicasting/Overview.html">multicasting</a>, ce qui permet de tisser des aspects sans la moindre intrusion au niveau du code à instrumenter. Concrètement, on pourrait se passer d&#8217;appliquer manuellement notre attribut <code>[NotifyPropertyChanged]</code> sur chaque type, et définir à la place une règle déterminant quels sont les types sur lesquels appliquer l&#8217;aspect.
<li>
<li>La méthode <code>ProvideAspects</code> est exécutée <em>lors de la post-compilation</em>. Elle permet de définir quels sont les sous-aspects qui composent le <code>CompoundAspect</code>, et sur quoi ils s&#8217;appliquent. Chaque sous-aspect doit être ajouté avec le descripteur de sa cible. Nous allons retrouver les 3 types d&#8217;aspect sus-cités :
<ul>
<li>Le sous aspect <code>ImplementINotifyPropertyChangedAspect</code> est appliqué au type de l&#8217;élément que l&#8217;on tisse.</li>
<li>Le sous aspect <code>FirePropertyChangedAspect</code> concerne les setters du type. On en ajoute donc une instance pour chacun d&#8217;entre eux.</li>
<li>Enfin, le sous aspect <code>InterceptFieldAffectationAspect</code> concerne les champs du type. On en ajoute une instance pour chacun des champs privés du type.</li>
</ul>
</li>
</ul>
<p>Regardons à quoi ressemble le premier des sous-aspects :</p>
<p><pre class="brush: csharp;">
[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;
    }
}
</pre></p>
<p>Ca colle assez bien avec les explications précédentes, et le code est auto commenté. Le composant implémentant réellement l&#8217;interface <code>INotifyPropertyChanged</code> est un <code>InnerNotifier</code> dont voici la définition :</p>
<p><pre class="brush: csharp;">
[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 &amp;&amp; this.shouldFirePropertyChanged)
        {
            this.PropertyChanged(this.eventSender, 
                                 new PropertyChangedEventArgs(propertyName));
        }
    }
}
</pre></p>
<p>Là encore c&#8217;est trivial : on implémente l&#8217;interface, et on expose une méthode publique permettant de déclencher manuellement l&#8217;évènement.</p>
<p>C&#8217;est d&#8217;ailleurs l&#8217;aspect <code>FirePropertyChangedAspect</code> qui s&#8217;en chargera :</p>
<p><pre class="brush: csharp;">
[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&lt;INotifyPropertyChanged&gt;;
            if (composedInteface != null)
            {
                this.notifier =
                    composedInteface.GetImplementation(eventArgs.InstanceCredentials)
                    as InnerNotifier;
            }
        }

        if (this.notifier != null)
        {
            this.notifier.FirePropertyChanged(this.propertyName);
        }
    }
}
</pre></p>
<p>La méthode <code>OnSuccess</code> est une des méthodes qu&#8217;il est possible de surdéfinir sur un <code>OnMethodBoundaryAspect</code>. Dans le flow d&#8217;interception, elle sera invoquée au runtime lorsque l&#8217;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&#8217;implémentation de cette méthode, mais sachez <em>au moins</em> qu&#8217;on récupére le <code>InnerNotifier</code> ayant été tissé par l&#8217;aspect <code>ImplementINotifyPropertyChangedAspect</code>, et que l&#8217;on appelle sa méthode <code>FirePropertyChanged</code> pour déclencher l&#8217;évenement en lui passant le nom de la propriété concernée.</p>
<p>Note: Fouillez dans la doc de PostSharp au sujet de <code>IComposed&lt;T&gt;</code> si vous voulez plus de viande, mais en gros, c&#8217;est le moyen d&#8217;exposer le composant d&#8217;un <code>CompositionAspect</code> (ici, notre <code>InnerNotifier</code>) lorsque l&#8217;option <code>CompositionAspectOptions.GenerateImplementationAccessor</code> a été définie. (cf. méthode <code>GetOptions</code> dans <code>ImplementINotifyPropertyChangedAspect</code>)</p>
<p>OK. Et non, ce n&#8217;est pas terminé, il reste bien le dernier sous-aspect utilisé pour déterminer si l&#8217;évenement <code>PropertyChanged</code> sera lancé ou non. J&#8217;ai nommé, <code>InterceptFieldAffectationAspect</code> :</p>
<p><pre class="brush: csharp;">
[Serializable]
public class InterceptFieldAffectationAspect : OnFieldAccessAspect
{
    private InnerNotifier notifier;

    public override void OnSetValue(FieldAccessEventArgs eventArgs)
    {
        if (this.notifier == null)
        {
            var composedInteface = eventArgs.Instance as IComposed&lt;INotifyPropertyChanged&gt;;
            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);
    }
}
</pre></p>
<p>Dans la méthode <code>OnSetValue</code> :</p>
<ul>
<li>On récupère une fois de plus le <code>InnerNotifier</code> ayant été tissé par l&#8217;aspect <code>ImplementINotifyPropertyChangedAspect</code></li>
<li>Si la valeur affectée au champ est égale à la valeur existante, on empêche le déclenchement de l&#8217;évènement via la propriété <code>ShouldFirePropertyChanged</code> du notifier.</li>
</ul>
<p>Et voilà.</p>
<h3>En résumé</h3>
<p>Hum, pas vraiment un résumé, mais plutôt la séquence ayant lieu au runtime lors de la modification d&#8217;une propriété d&#8217;un objet instrumenté :</p>
<ol>
<li>On s&#8217;abonne à l&#8217;évenement <code>PropertyChanged</code> de l&#8217;objet : ce dernier propage l&#8217;abonnement à son <code>InnerNotifier</code>.</li>
<li>On affecte une valeur à la propriété : le setter est appelé, mais l&#8217;interception par <code>FirePropertyChangedAspect</code> n&#8217;aura lieu qu&#8217;à la sortie de ce dernier.</li>
<li>Dans le corps du setter, on affecte la valeur au backing field : c&#8217;est une affectation interceptée par l&#8217;aspect <code>InterceptFieldAffectationAspect</code>. Si la valeur affectée est différente de celle du champ, alors il autorise le <code>InnerNotifier</code> à déclencher l&#8217;évènement <code>PropertyChanged</code> via la propriété <code>ShouldFirePropertyChanged</code>. Sinon, il désactive l&#8217;<code>InnerNotifier</code>, toujours via la propriété <code>ShouldFirePropertyChanged</code>.</li>
<li>A la sortie du setter, on entre dans le code d&#8217;interception de l&#8217;aspect <code>FirePropertyChangedAspect</code> qui, via l&#8217;<code>InnerModifier</code>, déclenche l&#8217;évènement <code>PropertyChanged</code>. Ce dernier ne sera effectivement déclenché que s&#8217;il a été laissé actif par l&#8217;<code>InterceptFieldAffectationAspect</code> précédemment.</li>
</ol>
<p>Quelques remarques pour les plus <strike>courageux</strike> désoeuvrés :</p>
<ul>
<li>L&#8217;implémentation n&#8217;est pas une référence, et la communication entre les différents sous-aspects via le <code>InnerNotifier</code> n&#8217;est pas quelque chose dont je suis particulièrement fier. Mais ma connaissance de PostSharp est trop limitée actuellement pour me permettre d&#8217;envisager quelque chose de plus élégant.</li>
<li>La solution peut sembler fastidieuse, mais compte tenu des spécifications (assez virtuelles, j&#8217;avoue), elle offre un bon compromis au niveau des performances : 5 fois plus lente que sans AOP, mais 5 fois plus rapide qu&#8217;une solution à base de tissage dynamique. Environ.<em> <strong>Update</strong> : Il semblerait que dans le cas de cette implémentation les performances soient les mêmes que celles qu&#8217;on pourrait obtenir avec un tisseur dynamique.</em></li>
<li>Au niveau de l&#8217;utilisation, c&#8217;est la forme d&#8217;AOP la plus concise et la moins intrusive possible, donc celle qui offre potentiellement le meilleur niveau d&#8217;abstraction et le meilleur gain de productivité.</li>
<li>Si vous avez des alternatives, avec ou sans PostSharp, je suis preneur, évidemment.</li>
</ul>
<p>Cet article est probablement trop chiant pour être lu jusqu&#8217;au bout, et trop spécifique pour être utile, mais il me fallait un post pour le mois d&#8217;octobre. Haha.</p>
<br />Publié dans Articles Tagged: .NET, AOP, C#, PostSharp, Tips <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/romainverdier.wordpress.com/319/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/romainverdier.wordpress.com/319/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/romainverdier.wordpress.com/319/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=codingly.com&amp;blog=3510695&amp;post=319&amp;subd=romainverdier&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://codingly.com/2008/10/29/utiliser-laop-avec-postsharp-pour-implementer-inotifypropertychanged/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/cb0ee4bde49708f4be24a02a5d59e52e?s=96&#38;d=http%3A%2F%2F0.gravatar.com%2Favatar%2Fad516503a11cd5ca435acc9bb6523536%3Fs%3D96" medium="image">
			<media:title type="html">Romain</media:title>
		</media:content>

		<media:content url="http://romainverdier.files.wordpress.com/2008/10/postsharp.png" medium="image">
			<media:title type="html">postsharp</media:title>
		</media:content>
	</item>
	</channel>
</rss>
