head.WriteLine()

Samstag, Februar 27, 2010

API-Design vom Feinsten

Neulich bin ich in MEF auf eine echte API-Design-Perle gestoßen.
Der folgende Code führt eine Composition der Parts durch:

public partial class MainWindow : Window
{
  ...
  private void Compose()
  {
    string path = Path.GetDirectoryName(
      Assembly.GetExecutingAssembly().Location);
    DirectoryCatalog catalog =
      new DirectoryCatalog(path);
    CompositionContainer container =
      new CompositionContainer(catalog);
    container.SatisfyImportsOnce(this);
  }
  ...
}

Das Problem hierbei ist, dass der oben stehende Code nicht in jedem Fall kompiliert werden kann. Schauen wir uns die entscheidende Stelle mal genauer an:

container.SatisfyImportsOnce(this);

Die Methode SatisfyImportsOnce() erwartet einen Parameter vom Typ ComposablePart. Nun leitet die oben deklarierte Klasse MainWindow jedoch nicht von ComposablePart ab. Somit ist schnell klar, warum der Code sich nicht kompilieren lässt – möchte man denken.
Er lässt sich jedoch fehlerfrei kompilieren, wenn man den Namespace System.ComponentModel.Composition eingebunden hat!
Dieser enthält nämlich eine Extension Method, die ebenfalls SatisfyImportsOnce() heißt, jedoch einen Parameter vom Typ object erwartet! CompositionContainer ist jedoch im Namespace System.ComponentModel.Composition.Hosting beheimatet, sodass die Verbindung zur Extension Method nicht unbedingt klar ist.

Ein schönes Beispiel dafür, wie man Sprach-Features dafür nutzen kann, den Entwickler maximal zu verwirren ;)