Laden...

Designfrage Serialisierung von Referenzen [==> Surrogate]

Erstellt von ThomasR vor 15 Jahren Letzter Beitrag vor 15 Jahren 5.932 Views
T
ThomasR Themenstarter:in
93 Beiträge seit 2007
vor 15 Jahren
Designfrage Serialisierung von Referenzen [==> Surrogate]

Hallo,

Ich muss zunächst ein wenig ausholen und den Grundaufbau/Funktion der Anwendung beschreiben:

Anwendung besteht vereinfacht aus 3 Komponenten:
Allen drei Komponenten ist eine gemeinsame Klasse bekannt, die ich hier mal IO nenne.

  1. Kommunikationsplugins: <Besitzen jeweils eine List<IO> und lesen/schreiben auf Anforderung die Werte der IOs der Liste in eine Hardware

  2. Anwendungskern: hier können zum Beispiel IOs erstellt werden und alle vorhandenen IOs werden hier in einem Steuerelement dargestellt.

  3. Manipulationsplugins: Diese besitzen jeweils ein Usercontrol und einen Dialog zur Konfiguration. Sie haben aufgaben wie z.b. das logische Verknüfung von IOs. Welche IOs verknüpft werden sollen werden im Konfigurationsdialog festgelegt.

Das ganze läuft nun folgendermaßen ab:
Der User kann sich per Drag & Drop beliebige Manipulationsplugins auf das Hauptfenster ziehen. Anschließend kann er dern Konfig öffnen und dort IOs ebenfalls per Drag & drop draufziehen.

So, meine Lösung dafür sieht so aus: Alle IO-Objekte existieren nur 1 mal und werden in der Hauptanwendung erzeugt. Bei der Konfiguration der Manipulationsplugins wird nur die Referenz des jeweiligen IOs übergenen. Gleichzeitig wird diese Referenz auch an das Kommunikationsplugin übergeben und an dessen Liste angehängt. Ich habe also dann je (benutztem) IO drei Referenzen darauf.

Falls ihr mein Konzeot für Schwachsinnig haltet oder eine bessere Idee habt, könnt ihr das natürlich gerne auch sagen, aber nun zum eigentlichen Thema:

Das ganze soll abgespeichert werden. Wenn ich aber nun alles einfach serialisiere, dann habe ich nach der Deserialisierung nicht mehr 1 Objekt mit 3 Referenzen sondern 3 Objekte. Ich denke mit automatischn .NET Serialisierung komme ich da nicht weit, richtig? So wirklich eine Idee habe ich aber nicht, wie ich das lösen könnte, dass die Referenzen nach dem Speichern/Laden noch passen.

Die einzige Möglichkeit die ich momentan sehe, ist die IOs Liste des Anwendungskern normal zu serialisieren aber bei den Manipulationsplugins nur eine eindeutige Bezeichnung abzuspeichern und beim Laden die Liste durchsuchen um die Zuordnung neu zu generieren. Das dürfte allerdings nicht gerade Performant (es kann durchaus mal ein paar hundert IOs geben) und außerdem aufwändig werden...

Habt ihr viellecht bessere Lösungvorschläge oder ganz andere Konzepte? Ich hoffe ich habe alles verständlich erklärt!

Gruß
Thomas

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo ThomasR,

der Ansatz mit der eindeutige Id klingt doch nicht schlecht und ich denke auch nicht, dass es da Performance-Probleme gibt.

Das Ganze klingt danach, als könnten hier Surrogate helfen. Ein Surrogat ist quasi ein Platzhalter für ein Objekt mit einer bestimmen Id. Ich habe mal eine Surrogat-Klasse skizziert, damit man sich das besser vorstellen kann.

Wenn man auf das Objekt, für das das Surrogat steht, zugreifen will, macht man das über surr.Instance.

Dabei wird zuerst geschaut, ob das Surrogat schon weiß, für welches Objekt es steht. In diesem Fall wird das Objekt sofort zurückgegeben.

Als nächstes wird geschaut, ob schon ein anderes Surrogat mit der gleichen Id weiß, für welches Objekt es steht. Dann wäre nämlich das Objekt schon in _dictAllInstances eingetragen. Auch in diesem Fall wird es sofort zurückgegeben.

Erst wenn das Objekt auch hier nicht gefunden ist, muss es geladen werden. Das ist auch gleichzeitig der einzige Schwachpunkt meiner Skizze. Denn das Laden muss ja durch die Klasse T erfolgen. Allerdings durch eine statische Methode, weil man ja gerade noch kein Objekt der Klasse T hat. Nun kann man für statische Methoden keine Interfaces definieren. Vielleicht muss man auf Reflection ausweichen und per Konvention verlangen, dass T eine Lade-Methode eines bestimmten Namens enthält. Vielleicht hat aber auch noch wer anders eine bessere Idee.


//*****************************************************************************
[Serializable]
public class Surrorgat <T> where T : class
{
   //--------------------------------------------------------------------------
   private static Dictionary <Object, T> _dictAllInstances
            = new Dictionary <Object, T> ();

   //--------------------------------------------------------------------------
   private Object _objId;

   //--------------------------------------------------------------------------
   [NonSerialized]
   private T _tInstance;

   //==========================================================================
   public Surrorgat (Object objId)
   {
      if (objId == null) { throw new ArgumentNullException ("objId"); }
      _objId = objId;
   }

   //==========================================================================
   public Object Id
   {
      get { return _objId; }
   }

   //==========================================================================
   public T Instance
   {
      get {
         if (_tInstance != null) {
            return _tInstance;
         }

         _tInstance = _dictAllInstances [_objId];
         if (_tInstance != null) {
            return _tInstance;
         }

         _tInstance = /* Objekt laden */;

         if (_tInstance == null) {
            throw new Exception ("Objekt konnte nicht geladen werden");
         }

         _dictAllInstances [_objId] = _tInstance;
         return _tInstance;
      }
   }
}

Suchhilfe: 1000 Worte

S
8.746 Beiträge seit 2005
vor 15 Jahren

Du verwendest vermutlich den XmlSerializer zur Serialisierung. Benutze stattdessen den SoapFormatter. Der unterstützt Objektidentitiät, arbeit also mit Referenzen.

T
ThomasR Themenstarter:in
93 Beiträge seit 2007
vor 15 Jahren

danke für die Antworten. Surrorgat hatte ich noch nie gehört, schau ich mir mal an.

Du verwendest vermutlich den XmlSerializer zur Serialisierung. Benutze stattdessen den SoapFormatter. Der unterstützt Objektidentitiät, arbeit also mit Referenzen.

Ich hatte mich eigentlich noch gar nicht auf einen bestimmten Serializer festgelegt. Ich kann mir irgendwie nicht vorstellen wie das gehen soll... Ich kann doch nicht beide Referenzen getrennt voneinander Serialisieren und Deserialisieren und danach zeigen beide wieder auf das gleiche Objekt, oder etwa doch?! Beim Deserialisieren werden doch in jedem Fall neue Objekte erzeugt, und nicht nur Referenzen...

S
8.746 Beiträge seit 2005
vor 15 Jahren

Beim Deserialisieren werden doch in jedem Fall neue Objekte erzeugt, und nicht nur Referenzen...

Klar werden Objekte erzeugt, aber bei Deserialisieren über den Formatter werden eben nur so viele Objekte erzeugt, wie zuvor serialisiert wurden. Im XML wirst du die entsprechenden Referenzen finden (id/idref). Beim XmlSerializer werden stattdessen Kopien erzeugt, und das schon beim Serialisieren.

Der SoapFormatter ist daher geeignet z.B. Graphen zu speichern und zu laden. Beim XmlSerializer ist das nicht der Fall. Dafür hat dieser andere Vorteile, u.a. die Performance.

A
5 Beiträge seit 2008
vor 15 Jahren

Hallo Thomas,
ich habe mal ein paar Tests zur Serialisierung gemacht und da viele Unterschiede festgestellt, was Sachen wie Referenzen oder Generics angeht.
Wenn du nicht speziell auf irgendeine manuelle Bearbeitungsmöglichkeit angewiesen bist, nutze den BinaryFormatter. Das sind nur ein paar Zeilen Code und alles was mit .NET geht wird auch im richtigen Format serialisiert und deserialisiert.

So sparst du dir eine Menge Arbeit und es klappt super! Probier es aus!

T
ThomasR Themenstarter:in
93 Beiträge seit 2007
vor 15 Jahren

Also ich habe das heute mal ausprobiert, auch wenn ich mir eigentlich schon im Voraus ziemlich sicher war, dass das so nicht funktionieren wird...

mein kleines Testsnippet in Pseudocode:


obj obj1 = new obj();
obj obj2 = obj1;

//jetzt habe ich identische Referenzen, die beide auf das Selbe Objekt zeigen

serialize(obj1);
serialize(obj2);
obj1 = deserialize();
obj2 = deserialize();

//jetzt zeigen die beiden Referenzen nicht mehr auf ein gemeinsames Objekt!!!


Ich schätze mal wir reden irgendwie aneinander vorbei?!
Verwendet wurde übrigens der binaryFormatter...

Gruß
Thomas

A
5 Beiträge seit 2008
vor 15 Jahren

Doch, ich weiß genau was du meinst. Ich habe mich mit dem selben Problem beschäftigt.
Ganz wichtig ist, dass du alles auf einmal serialisierst.
Aber wie geht das wenn man mehrere verschiedenen Objekte hat?
-> Die Antwort ist ganz einfach. Du musst eine Containter-Klasse bauen, die alle deine Objekte enthält, per Referenz. Dann serialisierst du nur die Container-Klasse und er nudelt dir alle Referenzen durch. Beim Deserialisieren erhältst du wieder genau das, was du brauchst.

Wenn du aber 2 mal getrennt serialisierst kann er ja gar nicht die Verbindung zwischen den Objekten managen. Wenn du sie aber beide verbindest als Unterobjekte, dann merkt er, dass er so was schon mal serialisiert hat und verweist mit einer Referenz drauf. Alles klar? 😉

T
ThomasR Themenstarter:in
93 Beiträge seit 2007
vor 15 Jahren

merci, werde das morgen mal testen! Das müsste ja dann in meinem Beispiel auch schon gehen, wenn ich obj1 und obj2 vor dem Serialisieren in ein ArrayList packe und anschließend wieder "auspacke", oder?

A
5 Beiträge seit 2008
vor 15 Jahren

Ganz genau. Wenn das die gleichen Objekte sind, dann kannst du sie in eine typisierte List schmeißen, das ist noch besser und schneller als der Container!

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Herbivore,
was ist wenn man zusätzlich einen Funktionszeiger/Delegaten zur Erstellung des Objekts übergibt? Oder eleganter:


public interface IObjectCreator<T> where T : class
{
  T CreateNewInstance(Object objectID);
}

public class Surrorgat <T, K>
        where T : class
        where K : class, IObjectCreator<T>, new()
{

   public T Instance
   {
      get {
         ...

         //_tInstance = /* Objekt laden */;
        K creator = new K();
        _tInstance = creator.CreateNewInstance(_objID);

         ...
      }
   }

}

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

klar, kann man so machen. Reflection war nur ein Vorschlag. Es gibt ja verschiedene Objekterzeugung-Pattern.

Hallo ThomasR,

was ich gestern noch unterschlagen habe. Man muss als Typ für die Id natürlich nicht Object verwenden, sondern kann dafür auch einen eigenen Typ-Parameter machen.

herbivore

T
ThomasR Themenstarter:in
93 Beiträge seit 2007
vor 15 Jahren

Hallo nochmal,

also, habe das getestet. Wenn alles in einen Container verpackt wird funktioniert die Serialisierung von identischen Referenzen. Wieder etwas dazugelernt, danke. In meinem speziellen Anwendungsfall wird es mir zwar nicht viel nutzen, da ja der eine Teil der Referenzen in einem Plugin ist, und die Hauptanwendung gar nicht weiß, was es im Plugin alles zu speichern gibt...Das Plugin muss seine Daten also selbst speichern, und da wird es dann nichts mit gemeinsamem Container... Zumindest sehe ich da bis jetzt noch keine Möglichkeit.

Gruß
Thomas

A
5 Beiträge seit 2008
vor 15 Jahren

Dann mach für jedes Plugin einen allgemeingültigen Container oder so. Diese Container steckst du wieder in einen Container. Wenn das allerdings nicht geht, dann wirst du um mehrfache Objekte wahrscheinlich nicht herum kommen.