
Als ersten Artikel unter dem Tag "Coder's Implementation {LANG}" beschäftige ich mich mit dem Umsetzen der in [Coder's Concept] Plugin-Architektur (v 1.0) beschriebenen Plugin-Architektur, in C#. Alle Artikel mit diesem Tag beziehen sich auf einen Artikel mit dem Tag "Coder's Concept". Implementierungsartikel versuche ich mit Klassendiagrammen zu anzureichern.
Das Projekt enthält drei Unterprojekte:
Das erste Projekt enthält die Typen, die vom Programm und von Plugins benötigt werden. Darin enthalten sind eine Klasse (Task) und eine Schnittstelle (ITaskFilter).
Die Schnittstelle muss von allen Plugins implementiert werden und wird als Anker vom Programm benutzt, um eine Liste der geladenen Plugins zusammenzustellen. Alle anderen Projekte referenzieren dieses Projekt. Dadurch sind diese beiden Typen in den beiden anderen Projekten verfügbar.
Das zweite Projekt enthält den Quellcode für das Pluginbasierte Programm.
Dieses Projekt besteht aus 5 nennenswerten Klassen:
Dieses Projekt enthält einen einfachen Filter, der nur bereits erledigte Aufgaben passieren lässt. Wichtig sind die Projekteinstellungen:
Damit der Filter zur Verfügung steht, muss er in das plugins-Verzeichnis kopiert werden, von wo aus er von der Klasse Program im Projekt TodoApp geladen wird. Daher ist es notwendig, dass dieser Ordner im Unterverzeichnis bin/debug existiert. In der an diesem Artikel verlinkten Zip-Datei ist der Ordner vorhanden.
Assemblies nachträglich zu laden ist einfach.
string[] filenames = Directory.GetFiles(Directory.GetCurrentDirectory()+"\plugins");
foreach (string filename in filenames)
{
Assembly asm = Assembly.LoadFile(filename);
foreach (Type t in asm.GetTypes())
{
if (t.IsClass && t.GetInterface(typeof(ITaskFilter).Name).Equals(typeof(ITaskFilter)))
{
plugins.Add((ITaskFilter)t.GetConstructor(Type.EmptyTypes).Invoke(null));
}
}
}
Zunächst werden alle potentiellen Assemblies festgestellt (das erledigt das äußerste foreach) und geladen. Danach werden alle Typen in dem geladenen Assembly betrachtet. Wenn es sich um eine Klasse handelt und das notwendige Interface implementiert wird, so wird das Objekt instanziert und abgelegt. Innerhalb des Programms wird immer nur über das Interface mit den Plugins kommuniziert.
Der angehängte Code ist kommentiert und sollte keine Fragen offen lassen.