Dynamisch geladene Typen serialisieren

Shakie

Erfahrenes Mitglied
Vorab: Ich habe für mein Problem schon eine Lösung, aber ich finde sie nicht schön.
Zum Problem:
Ich habe eine Art Plugin-System. Ich lade eine DLL dynamisch und instanziiere eine Klasse aus dieser DLL. Diese Instanz weise ich einem Member einer anderen Klasse aus einer anderen Assembly zu:
C#:
Assembly a = Assembly.LoadFile("C:\\ClassLibrary1.dll");
object plugin = a.CreateInstance("ClassLibrary1.PluginKlasse");
LokaleKlasse lokal = new LokaleKlasse();
lokal.plugin = plugin;
Wobei "LokaleKlasse" so definiert ist:
C#:
// Assembly Hauptprogramm.exe
public class LokaleKlasse
{
    public object plugin { get; set; }
}
Und PluginKlasse so:
C#:
// Assembly ClassLibrary1
public class PluginKlasse
{
    public String Information {get; set; }
}
Nun möchte ich die Instanz "lokal" mit dem XmlSerializer serialisieren, wo das Problem anfängt. Der XmlSerializer weiß nicht, was er mit dem plugin-Member tun soll, da es in "LokaleKlasse" lediglich als Object definiert ist.
Glücklicherweise gibt es einen überladenen Konstruktor des XmlSerializers, der ein Array von zusätzlichen Typen erwartet. Der XmlSerializer sollte dann prüfen, ob ein Instanztyp, mit der er nichts anfangen kann, vielleicht ein Typ aus diesem Array ist. Sollte... Das funktioniert nur, wenn der Basistyp nicht System.Object ist, sondern irgendwas anderes, was in der selben Assembly definiert ist wie die Klasse "LokaleKlasse". Jedenfalls war das bei meinen Tests so.
Meine Frage am Ende dieses Posts wird sein, ob das nicht auch hübscher geht als so, wie ich es jetzt gelöst habe: nämlich in der Assembly von LokaleKlasse eine "LeereKlasse" zu definieren, die nichts weiter tut außer zu existieren und von der meine Plugin-Klasse (PluginKlasse) ableitet:
C#:
// Assembly Hauptprogramm.exe
public class LeereKlasse {}

public class LokaleKlasse
{
    public LeereKlasse plugin { get; set; }
}
C#:
// Assembly ClassLibrary1:
public class PluginKlasse : Hauptprogramm.LeereKlasse
{
    public String Information {get; set; }
}
Die Serialisierung funktioniert dann problemlos so:
C#:
// Zuerst dynamisch eine Instanz erzeugen (wie oben)
Assembly a = Assembly.LoadFile("C:\\ClassLibrary1.dll");
object plugin = a.CreateInstance("ClassLibrary1.PluginKlasse");
LokaleKlasse lokal = new LokaleKlasse();
lokal.plugin = plugin;
// Serialisierer instanziieren
XmlSerializer ser = new XmlSerializer(typeof(LokaleKlasse), new Type[] {lokal.plugin.GetType() });
// StreamWriter zum Schreiben in Datei instanziieren
using (System.IO.StreamWriter writer = new System.IO.StreamWriter("C:\\test.xml"))
    {
        // und letztendlich serialisieren
        ser.Serialize(writer, lokal);
    }
Aber ist das wirklich so gedacht, dass ich den Umweg über die "LeereKlasse" gehen muss? Man sollte meinen, dass ich eine leere Klasse einfach durch den Typ System.Object hätte ersetzen können...
 
Muss es XMlSerialize sein ich würde eher BinaryFormatter (oder SoapFormatter, abermit dem habe ich noch nicht gearbeitet) verwenden.

Der geht nicht auf die Eigenschaften sondern auf die Felder und serialisiert diese.
Der würde auch mit objekt klar kommen solange das SerializableAttribute oder ISerializable bei dem Objekt gesetzt ist.

Darüber hinaus musst du beim XmlSerializer bei Enums aufpassen da dieser nicht den Feldwert speichert, sondern den Feldnamen.
Hatte mir schon einige Probleme bereitet (Gibt aber ein Attribute um dies zu ändern, XmlEnumAttribute glaube).
 
Es kann sein, dass die Klasse in eine Datei serialisiert wird und beim Deserialisieren aber nicht mehr alle Plugins vorhanden sind (z.B. weil der Benutzer eine Plugin-DLL gelöscht hat). Es soll dann aber immer noch möglich sein die Datei halbwegs zu laden, so gut es halt geht. Mit dem BinaryFormatter wüsste ich nicht wie ich das anstelle. Ich hoffe, dass das mit dem XmlSerializer besser geht - ich habe mich noch nicht so detailliert mit ihm beschäftigt.

Und vor IXmlSerializable habe ich mich gescheut, da
  1. ich dann für alle Child-Objekte ebenfalls IXmlSerializable implementieren muss
  2. und mir die korrekte Implementierung des Interfaces aufwendig erscheint, nachdem ich das hier gelesen habe
Ich weiß nicht so recht für welche Vorgehensweise ich mich nun entscheiden soll :confused:
EDIT: Bezüglich SoapFormatter, da musste ich mir erst mal den Wiki-Eintrag durchlesen, wass SOAP überhaupt ist:
Wikipedia hat gesagt.:
SOAP ist ein Protokoll zum Austausch XML-basierter Nachrichten [...]
Ich vermute, intern benutzt der SoapFormatter also erst einen XMl-Serializer?
 
Zuletzt bearbeitet:
Keine Ahnung ^^

Zum BinaryFormatter:
Am besten ist es die ISerializable Schnittstelle bei deinem Plugin System zu implementieren.

Über die GetObjectData Methode setzt du einfach die Werte ins SerializationInfo die serialisiert werden soll.
Das wichtige hierbei ist aber eigentlich das man einen Kostructor anlegen muss der SerializationInfo und StreamingContext context als Parameter erwartet um die Daten aus SerializationInfo wieder auszulesen.

Sprich du bist nicht mehr ganz Klassen und Namespace abhängig.
 

Neue Beiträge

Zurück