Apr 152011
 

WPF-DataBinding rocks! Schön ist auch, dass es sowohl mit den (relativ neuen) DependencyProperties als auch mit den guten alten CLR-Properties (POCOs = Plain old CLR-Objects) gut harmoniert, wenn man denn INotifyPropertyChanged aus dem ComponentModel implementiert.
Das ist auch nicht schwierig, sorgt aber immer wieder für Diskussionen, wenn es darum geht, wie man am besten MagicStrings (PropertyNamen) vermeidet, blöde Copy/Paste-Fehler verhindert (z.B. BackupFieldName statt PropertyName in den EventArgs), ob man Reflection einsetzt (Expressions statt MagicString, Performance-Hit?) usw.
Eine Muster-Implementierung, aber was noch viel wertvoller ist, eine Diskussion verschiedener Ansätze findet sich im OpenSource-Project CompositeExtensions.

Vermutlich jedes GUI-Framework  bietet mindestens einen Lösungsansatz dafür.
Heute habe ich noch einen weiteren gefunden: den NotifyPropertyWeaver.

Das Konzept ist nicht so überraschend:
Die Properties werfen keinerlei Events mehr im explizit geschriebenen (Boilerplate-)Code. Stattdessen tragen sie eine recht kompakte Konfiguration an Klassen und Properties, z.B. AOP-like über Attributes.
Diese muss man natürlich später von irgendwem noch in Code umgewandelt werden, und genau dafür gibt es dann auch wieder zahlreiche Ansätze:

  • Ein DynamicProxy könnte Property-Aufrufe abfangen und z.B. zusätzliche Aufrufe hinzufügen, Events auslösen etc. (Sehr stylischer Artikel dazu)
  • Basisklassen können die Konfiguration umsetzen. Das hat natürlich den großen Nachteil, dass man von dieser Klasse ableiten muss und keine andere Basisklasse verwenden kann.
  • Der PropertyWeaver setzt auf einen Post-Compile-Task: Nachdem der eigentliche Code (Properties werfen keine Events) kompiliert ist, bearbeitet der PropertyWeaver die generierte (IL-)Assembly nach und ergänzt sie um die Event-Aufrufe etc. Dabei sind auch komplexere Situationen möglich, wie z.B. abhängie Properties, für die auch dann ein Event geworfen werden muss, wenn sich der Wert einer bestimmten anderen Property ändert.

    Vorteil: Der generierte Code sieht quasi genau so aus wie handgeschriebener Boilerplate-Code, und ist damit auch zur Laufzeit gleichwertig. Hilfsmittel wie Reflection werden zur Laufzeit nicht benötigt, die statisch vorhandere Information wird in statischen Code umgewandelt, feine Sache. Die Nachteile des tatsächlich manuell fabrizierten Codes, nämlich die ständigen Fehlerquellen Typo und Copy/Paste sind quasi eliminiert, da der Code ja maschinell erzeugt wird. Ok, ein Restrisiko bleibt durch fehlerhafte Konfiguration, aber ein Bisschen muss man halt schon sagen was man will, wenn man’s auch kriegen möchte (Garbage In, Garbage Out).

    Leider gibt’s bei aller Eleganz dieser Lösung auch Nachteile – There ain’t no free lunch:
    Der Code wird erst Post-Compile ergänzt, er landet also nicht im Code-File in der IDE sondern nur in der erzeugten dll. Somit ist er dann auch nicht zugänglich für z.B. statische Code-Analyse (Resharper, StyleCop, etc.), und beim „Lesen“ des Codes muss man immer beachten, dass evtl. noch mehr passiert als dort steht.
    Glücklicherweise werden die zugehörigen pdb-Files ebenfalls ergänzt, Debugging funktioniert also trotzdem. Und Unit-Tests durchlaufen natürlich auch den endgültigen Code, der Post-Compile-ergänzte Teil wird also mitgetestet.