Übersicht über die Designerserialisierung

Mithilfe der Designerserialisierung können Sie zur Entwurfszeit oder zur Laufzeit den Zustand der Komponenten beibehalten.

Serialisierung für Objekte

.NET Framework unterstützt mehrere Arten der Serialisierung, z. B. Codegenerierung, SOAP-Serialisierung, binäre Serialisierung und XML-Serialisierung.

Die Designerserialisierung stellt eine spezielle Art der Serialisierung dar, die die normalerweise Entwicklungstools zugeordnete Objektdauerhaftigkeit umfasst. Bei der Designerserialisierung wird ein Objektdiagramm in eine Quelldatei konvertiert, die später für die Wiederherstellung des Objektdiagramms verwendet werden kann. Eine Quelldatei kann Code, Markup oder sogar SQL-Tabelleninformationen enthalten. Die Designerserialisierung ist auf alle Common Language Runtime-Objekte anwendbar.

Die Designerserialisierung unterscheidet sich in mehrfacher Hinsicht von der typischen Objektserialisierung:

  • Damit die Entwurfszeitlogik aus einer Komponente entfernt werden kann, ist das die Serialisierung ausführende Objekt vom Laufzeitobjekt getrennt.

  • Das Serialisierungsschema wurde unter der Voraussetzung entworfen, dass das Objekt in vollständig initialisiertem Zustand erstellt und anschließend während der Serialisierung durch Eigenschaften- und Methodenaufrufe verändert wird.

  • Eigenschaften eines Objekts mit Werten, die niemals für das Objekt festgelegt wurden, werden nicht serialisiert. Umgekehrt initialisiert der Deserialisierungsstream möglicherweise nicht alle Eigenschaftswerte. Eine detailliertere Beschreibung der Serialisierungsregeln finden Sie im Abschnitt "Allgemeine Serialisierungsregeln" weiter unten in diesem Thema.

  • Die Qualität des Inhalts im Serialisierungsstream wird höher gewichtet als die vollständige Serialisierung eines Objekts. Wenn keine definierte Methode zur Serialisierung eines Objekts vorhanden ist, wird dieses Objekt übersprungen, ohne dass eine Ausnehme ausgelöst wird. Die Designerserialisierung bietet Möglichkeiten zur Serialisierung eines Objekts in einfachem, lesbaren Format anstatt in Form eines nicht transparenten BLOBs.

  • Der Serialisierungsstream enthält möglicherweise mehr Daten, als für die Deserialisierung benötigt werden. Bei der Serialisierung von Quellcode ist z. B. der für die Deserialisierung eines Objektdiagramms benötigte Code mit Benutzercode vermischt. Dieser Benutzercode muss bei der Serialisierung beibehalten und bei der Deserialisierung übersprungen werden.

Tipp

Designerserialisierung kann zur sowohl zur Laufzeit als auch zur Entwurfszeit verwendet werden.

In der folgenden Tabelle sind die Entwurfsziele aufgeführt, die über die .NET Framework-Designerserialisierungsinfrastruktur erfüllt werden.

Entwurfsziel

Beschreibung

Modular

Durch die Erweiterung des Serialisierungsprozesses können neue Datentypen einbezogen werden, die wiederum nützliche und leicht lesbare Selbstbeschreibungen bereitstellen können.

Leicht erweiterbar

Der Serialisierungsprozess ist leicht erweiterbar, damit neue Datentypen einbezogen werden können.

Formatneutral

Objekte können in einer Vielzahl unterschiedlicher Dateiformate eingebunden werden und die Designerserialisierung ist nicht an ein bestimmtes Dateiformat gebunden.

Architektur

Die Designerserialisierungsarchitektur basiert auf Metadaten, Serialisierungsprogrammen und einem Serialisierungs-Manager. In der folgenden Tabelle sind die Rollen der einzelnen Aspekte der Architektur beschrieben.

Aspekt

Beschreibung

Metadatenattribute

Ein Typ T wird über ein Attribut mit einem Serialisierungsprogramm S verknüpft. Darüber hinaus unterstützt die Architektur ein Bootstrapper-Attribut zum Installieren eines Objekts, das Serialisierungsprogramme für Typen bereitstellen kann, die selbst über keine solchen Programme verfügen.

Serialisierungsprogramme

Ein Serialisierungsprogramm ist ein Objekt, das einen bestimmten Typ oder eine Reihe von Typen serialisieren kann. Für jedes Datenformat ist eine Basisklasse vorhanden. Zum Beispiel gibt es möglicherweise eine DemoXmlSerializer-Basisklasse, die ein Objekt in XML konvertieren kann. Die Architektur ist nicht von einem bestimmten Serialisierungsformat abhängig und sie umfasst zusätzlich eine auf dem Code Document Object Model (CodeDOM) beruhende Implementierung derselben Architektur.

Serialisierungs-Manager

Ein Serialisierungs-Manager ist ein Objekt, das einen Informationsspeicher für alle unterschiedlichen Serialisierungsprogramme bereitstellt, die für die Serialisierung eines Objektdiagramms verwendet werden. Ein Diagramm mit 50 Objekten verfügt möglicherweise über 50 verschiedene Serialisierungsprogramme, die jeweils eine eigene Ausgabe generieren. Der Serialisierungs-Manager ermöglicht die Kommunikation dieser Serialisierungsprogramme miteinander.

Anhand der folgenden Abbildung und Prozedur wird veranschaulicht, wie Objekte in einem Diagramm, in diesem Fall A und B, serialisiert werden können.

Serialisieren eines Objektdiagramms

Serialisieren von Objekten in einem Diagramm

  1. Der Aufrufer fordert vom Serialisierungs-Manager ein Serialisierungsprogramm für Objekt A an:

    MySerializer s = manager.GetSerializer(a);
    
  2. Das Metadatenattribut auf dem Objekttyp von A verknüpft A mit einem Serialisierungsprogramm des angeforderten Typs. Anschließend fordert der Aufrufer das Serialisierungsprogramm zum Serialisieren von A auf:

    Blob b = s.Serialize(manager, a);
    
  3. Das Serialisierungsprogramm des Objekts A serialisiert A. Für jedes während der Serialisierung von A gefundene Objekt fordert es vom Serialisierungs-Manager weitere Serialisierungsprogramme an:

    MySerializer s2 = manager.GetSerializer(b);
    Blob b2 = s2.Serialize(manager, b);
    
  4. Das Ergebnis der Serialisierung wird an den Aufrufer zurückgegeben:

    Blob b = ...
    

Allgemeine Serialisierungsregeln

Eine Komponente macht normalerweise eine Reihe von Eigenschaften verfügbar. Zum Beispiel verfügt das Button-Steuerelement in Windows Forms über Eigenschaften wie die BackColor-Eigenschaft, die ForeColor-Eigenschaft und die BackgroundImage-Eigenschaft. Wenn Sie ein Button-Steuerelement auf einem Formular in einem Designer platzieren und den generierten Code anzeigen, werden Sie feststellen, dass nur eine Teilmenge der Eigenschaften im Code beibehalten wurde. Normalerweise handelt es sich dabei um die Eigenschaften, für die Sie explizit einen Wert festgelegt haben.

Der dem Button-Steuerelement zugeordnete CodeDomSerializer definiert das Serialisierungsverhalten. In der folgenden Liste werden einige der Regeln beschrieben, nach denen der CodeDomSerializer den Wert einer Eigenschaft serialisiert:

  • Wenn der Eigenschaft ein DesignerSerializationVisibilityAttribute angefügt ist, bestimmt das Serialisierungsprogramm über dieses Attribut, ob die Eigenschaft serialisiert wird (z. B. Visible oder Hidden), und wie die Serialisierung erfolgen soll (Content).

  • Über den Visible-Wert oder den Hidden-Wert wird angegeben, ob die Eigenschaft serialisiert wird. Der Content-Wert gibt an, wie die Eigenschaft serialisiert wird.

  • Wenn die Komponente für eine Eigenschaft mit dem Namen DemoProperty eine Methode mit dem Namen ShouldSerializeDemoProperty implementiert, bestimmt die Entwurfsumgebung über einen spät gebundenen Aufruf an diese Methode, ob die Serialisierung durchgeführt werden soll. Die Methode für die BackColor-Eigenschaft wird z: B. mit ShouldSerializeBackColor bezeichnet.

  • Wenn für die Eigenschaft ein DefaultValueAttribute angegeben ist, wird der Standardwert mit dem aktuellen Wert in der Komponente verglichen. Die Eigenschaft wird nur dann serialisiert, wenn der aktuelle Wert nicht der Standardwert ist.

  • Der der Komponente zugeordnete Designer kann auch insofern an der Entscheidung, ob die Serialisierung stattfinden soll, teilhaben, indem er Eigenschaften spiegelt oder selbst ShouldSerialize-Methoden implementiert.

Tipp

Das Serialisierungsprogramm überträgt die Entscheidung zur Serialisierung an den der Eigenschaft zugeordneten PropertyDescriptor und der PropertyDescriptor verwendet dann die oben aufgeführten Regeln.

Wenn Sie die Komponente auf andere Art und Weise serialisieren möchten, können Sie eine eigene, von CodeDomSerializer abgeleitete Klasse von Serialisierungsprogrammen schreiben und sie mithilfe des DesignerSerializerAttribute der Komponente zuordnen.

Intelligente Implementierung von Serialisierungsprogrammen

Eine der Anforderungen an den Entwurf von Serialisierungsprogrammen besteht darin, dass sobald ein neues Serialisierungsformat benötigt wird, sämtliche Datentypen zur Unterstützung dieses Formats mit einem Metadatenattribut aktualisiert werden müssen. Diese Anforderung kann jedoch durch die Verwendung von Serialisierungsanbietern gekoppelt mit Serialisierungsprogrammen, die generische Objektmetadaten verwenden, erfüllt werden. In diesem Abschnitt wird die Methode beschrieben, die bevorzugt für den Entwurf eines Serialisierungsprogramms für ein bestimmtes Format verwendet werden sollte, damit möglichst wenig benutzerdefinierte Serialisierungsprogramme benötigt werden.

Das folgende Schema definiert ein hypothetisches XML-Format, zu dem ein Objektdiagramm beibehalten wird.

<TypeName>
    <PropertyName>
        ValueString
    </PropertyName>
</TypeName>

Dieses Format wird mithilfe einer erfundenen Klasse mit dem Namen DemoXmlSerializer serialisiert.

public abstract class DemoXmlSerializer 
{
    public abstract string Serialize(
        IDesignerSerializationManager m, 
        object graph);
}

Beachten Sie, dass es sich bei DemoXmlSerializer um eine modulare Klasse handelt, die eine Zeichenfolge aus einzelnen Elementen erzeugt. Der DemoXmlSerializer für den Datentyp Int32 gibt z. B. die Zeichenfolge "23" zurück, wenn ihm eine ganze Zahl mit dem Wert 23 übergeben wird.

Serialisierungsanbieter

Das vorherige Schemabeispiel hat verdeutlicht, dass zwei grundlegende Typen behandelt werden müssen:

  • Objekte mit untergeordneten Eigenschaften.

  • Objekte, die in Text konvertiert werden können.

Es wäre kompliziert, jeder Klasse ein benutzerdefiniertes Serialisierungsprogramm hinzuzufügen, mit dem diese Klasse in Text oder XML-Tags konvertiert werden kann. Serialisierungsanbieter stellen zur Lösung dieses Problems einen Rückrufmechanismus zur Verfügung, über den ein Objekt ein Serialisierungsprogramm für einen gegebenen Typ bereitstellen kann. In diesem Beispiel ist die verfügbare Gruppe von Typen durch folgende Bedingungen eingeschränkt:

  • Wenn der Typ über die IConvertible-Schnittstelle in eine Zeichenfolge konvertiert werden kann, wird der StringXmlSerializer verwendet.

  • Wenn der Typ nicht in eine Zeichenfolge konvertiert werden kann, sondern öffentlich ist und über einen leeren Konstruktor verfügt, wird der ObjectXmlSerializer verwendet.

  • Wenn keines von beidem zutrifft, gibt der Serialisierungsanbieter null zurück und zeigt damit an, dass kein Serialisierungsprogramm für das Objekt zur Verfügung steht.

Das folgende Codebeispiel veranschaulicht, wie das aufrufende Serialisierungsprogramm entscheidet, was beim Auftreten dieses Fehlers geschehen soll.

internal class XmlSerializationProvider : IDesignerSerializationProvider 
{
    object GetSerializer(
        IDesignerSerializationManager manager, 
        object currentSerializer, 
        Type objectType, 
        Type serializerType) 
    {

        // Null values will be given a null type by this serializer.
        // This test handles this case.
        if (objectType == null) 
        {
            return StringXmlSerializer.Instance;
        }

        if (typeof(IConvertible).IsSubclassOf(objectType)) 
        {
            return StringXmlSerializer.Instance;
        }

        if (objectType.GetConstructor(new object[]) != null) 
        {
            return ObjectXmlSerializer.Instance;
        }

        return null;
    }
}

Nachdem ein Serialisierungsanbieter definiert ist, muss er in Verwendung genommen werden. Ein Serialisierungsanbieter kann einem Serialisierungs-Manager über die AddSerializationProvider-Methode hinzugefügt werden, dazu muss dieser Aufruf allerdings an den Serialisierungs-Manager erfolgen. Um einem Serialisierungs-Manager automatisch einen Serialisierungsanbieter hinzuzufügen, müssen Sie dem Serialisierungsprogramm ein DefaultSerializationProviderAttribute hinzufügen. Dieses Attribut erfordert, dass der Serialisierungsanbieter über einen öffentlichen, leeren Konstruktor verfügt. Im folgenden Codebeispiel wird die notwendige Änderung am DemoXmlSerializerveranschaulicht.

[DefaultSerializationProvider(typeof(XmlSerializationProvider))]
public abstract class DemoXmlSerializer 
{
}

Immer dann, wenn von einem Serialisierungs-Manager ein beliebiger Typ von DemoXmlSerializer angefordert wird, wird dem Serialisierungs-Manager nun der Standard-Serialisierungsanbieter hinzugefügt, sofern dies nicht bereits geschehen ist.

Serialisierungsprogramme

Die Beispielklasse DemoXmlSerializer verfügt über zwei konkrete Klassen von Serialisierungsprogrammen mit den Namen StringXmlSerializer und ObjectXmlSerializer. Im folgenden Codebeispiel wird die Implementierung des StringXmlSerializer veranschaulicht.

internal class StringXmlSerializer : DemoXmlSerializer
{
    internal StringXmlSerializer Instance = new StringXmlSerializer();

    public override string Serialize(
        IDesignerSerializationManager m, 
        object graph) 
    {

        if (graph == null) return string.Empty;

        IConvertible c = graph as IConvertible;
        if (c == null) 
        {
            // Rather than throwing exceptions, add a list of errors 
            // to the serialization manager.
            m.ReportError("Object is not IConvertible");
            return null;
        }

    return c.ToString(CultureInfo.InvariantCulture);
    }
}

Die Implementierung des ObjectXmlSerializer ist komplizierter, da in diesem Fall die Enumeration der öffentlichen Eigenschaften des Objekts erforderlich ist. Im folgenden Codebeispiel wird die Implementierung des ObjectXmlSerializer veranschaulicht.

internal class ObjectXmlSerializer : DemoXmlSerializer 
{
    internal ObjectXmlSerializer Instance = new ObjectXmlSerializer();

    public override string Serialize(
        IDesignerSerializationManager m, 
        object graph) 
    {

        StringBuilder xml = new StringBuilder();
        xml.Append("<");
        xml.Append(graph.GetType().FullName);
        xml.Append(">");

        // Now, walk all the properties of the object.
        PropertyDescriptorCollection properties;
        Property p;

        properties = TypeDescriptor.GetProperties(graph);

        foreach(p in properties) 
        {
            if (!p.ShouldSerializeValue(graph)) 
            {
                continue;
            }

            object value = p.GetValue(graph);
            Type valueType = null;
            if (value != null) valueType = value.GetType();

            // Get the serializer for this property
            DemoXmlSerializer s = m.GetSerializer(
                valueType, 
                typeof(DemoXmlSerializer)) as DemoXmlSerializer;

            if (s == null) 
            {
                // Because there is no serializer, 
                // this property must be passed over.  
                // Tell the serialization manager
                // of the error.
                m.ReportError(string.Format(
                    "Property {0} does not support XML serialization",
                    p.Name));
                continue;
            }

            // You have a valid property to write.
            xml.Append("<");
            xml.Append(p.Name);
            xml.Append(">");

            xml.Append(s.Serialize(m, value);

            xml.Append("</");
            xml.Append(p.Name);
            xml.Append(">");
        }

        xml.Append("</");
        xml.Append(graph.GetType().FullName);
        xml.Append(">");
        return xml.ToString();
    }
}

ObjectXmlSerializer ruft für jeden Eigenschaftswert andere Serialisierungsprogramme auf. Das hat zwei Vorteile. Zum einen ermöglicht dies die sehr einfache Strukturierung von ObjectXmlSerializer, zum anderen wird ein Erweiterungspunkt für Typen von Drittanbietern bereitgestellt. Wenn ObjectXmlSerializer mit einem Typ dargestellt wird, den keines der beiden Serialisierungsprogramme schreiben kann, kann für diesen Typ ein benutzerdefiniertes Serialisierungsprogramm bereitgestellt werden.

Verwendung

Zur Verwendung dieser neuen Serialisierungsprogramme muss eine Instanz von IDesignerSerializationManager erstellt werden. Von dieser Instanz aus fordern Sie ein Serialisierungsprogramm an und fordern dann beim Serialisierungsprogramm die Serialisierung der Objekte an. Im folgenden Codebeispiel wird Rectangle für ein zu serialisierendes Objekt verwendet, da dieser Typ über einen leeren Konstruktor sowie über vier Eigenschaften verfügt, die IConvertible unterstützen. Anstatt den IDesignerSerializationManager selbst zu implementieren, können Sie die vom DesignerSerializationManager bereitgestellte Implementierung verwenden.

Rectangle r = new Rectangle(5, 10, 15, 20);
DesignerSerializationManager m = new DesignerSerializationManager();
DemoXmlSerializer x = (DemoXmlSerializer)m.GetSerializer(
    r.GetType(), typeof(DemoXmlSerializer);

string xml = x.Serialize(m, r);

Dadurch würde die folgende XML erstellt werden.

<System.Drawing.Rectangle>
<X>
5
</X>
<Y>
10
</Y>
<Width>
15
</Width>
<Height>
15
</Height>
</System.Drawing.Rectangle>

Tipp

Die XML ist nicht eingezogen. Der Einzug kann problemlos über die Context-Eigenschaft im IDesignerSerializationManager ausgeführt werden. Jede Serialisierungsprogrammebene kann dem Kontextstapel ein Objekt mit der aktuellen Einzugsebene hinzufügen, und jedes Serialisierungsprogramm kann den Stapel nach diesem Objekt durchsuchen und es für die Bereitstellung eines Einzugs verwenden.

Siehe auch

Referenz

IDesignerSerializationManager

DesignerSerializationManager

DefaultSerializationProviderAttribute

IConvertible

CodeDomSerializerBase

Weitere Ressourcen

Erweitern der Entwurfszeitunterstützung