Benutzerdefinierte SerialisierungCustom serialization

Unter benutzerdefinierter Serialisierung wird die Steuerung der Serialisierung und Deserialisierung eines Typs verstanden.Custom serialization is the process of controlling the serialization and deserialization of a type. Durch die Steuerung der Serialisierung lässt sich die Serialisierungskompatibilität sicherstellen. Damit ist die Fähigkeit gemeint, Objekte unterschiedlicher Versionen eines Typs serialisieren und deserialisieren zu können, ohne die Kernfunktionalität des Typs zu beeinträchtigen.By controlling serialization, it's possible to ensure serialization compatibility, which is the ability to serialize and deserialize between versions of a type without breaking the core functionality of the type. Zum Beispiel kann die erste Version eines Typs nur zwei Felder umfassen.For example, in the first version of a type, there may be only two fields. In der nächsten Version des Typs werden mehrere weitere Felder hinzugefügt.In the next version of a type, several more fields are added. Die zweite Version einer Anwendung muss jedoch fähig sein, beide Typen zu serialisieren und zu deserialisieren.Yet the second version of an application must be able to serialize and deserialize both types. In den folgenden Abschnitten wird beschrieben, wie Serialisierung gesteuert wird.The following sections describe how to control serialization.

Warnung

Die binäre Serialisierung kann gefährlich sein.Binary serialization can be dangerous. Deserialisieren Sie niemals Daten aus nicht vertrauenswürdigen Quellen, und führen Sie niemals eine Schleife für serialisierte Daten in Systeme durch, die Sie nicht steuern.Never deserialize data from an untrusted source and never round-trip serialized data to systems not under your control.

Wichtig

In Versionen vor .NET Framework 4.0 wurde die Serialisierung benutzerdefinierter Benutzerdaten in einer teilweise vertrauenswürdigen Assembly mit GetObjectData erreicht.In versions previous to .NET Framework 4.0, serialization of custom user data in a partially trusted assembly was accomplished using the GetObjectData. Ab Version 4 wird diese Methode mit dem SecurityCriticalAttribute-Attribut markiert, das die Ausführung in teilweise vertrauenswürdigen Assemblys verhindert.Starting with version 4.0, that method is marked with the SecurityCriticalAttribute attribute which prevents execution in partially trusted assemblies. Um diese Bedingung zu umgehen, implementieren Sie die ISafeSerializationData-Schnittstelle.To work around this condition, implement the ISafeSerializationData interface.

Ausführen von benutzerdefinierten Methoden während und nach der SerialisierungRunning custom methods during and after serialization

Die empfohlene und einfachste Vorgehensweise (eingeführt in Version 2.0 von .NET Framework) ist es, die folgenden Attribute für Methoden zu verwenden, die zum Korrigieren von Daten während und nach der Serialisierung verwendet werden:The best practice and easiest way (introduced in version 2.0 of the .NET Framework) is to apply the following attributes to methods that are used to correct data during and after serialization:

Diese Attribute ermöglichen es dem Typ, an jeder einzelnen Phase bzw. an allen vier Phasen des Serialisierungs- und Deserialisierungsprozesses teilzuhaben.These attributes allow the type to participate in any one of, or all four of the phases, of the serialization and deserialization processes. Die Attribute geben die Methoden des Typs an, die während jeder Phase aufgerufen werden sollen.The attributes specify the methods of the type that should be invoked during each phase. Die Methoden greifen nicht auf den Serialisierungsstream zu, sondern ermöglichen es Ihnen, das Objekt vor und nach der Serialisierung bzw. vor oder nach der Deserialisierung zu ändern.The methods do not access the serialization stream but instead allow you to alter the object before and after serialization, or before and after deserialization. Die Attribute können auf allen Vererbungshierarchieebenen des Typs verwendet werden. Jede Methode wird in der Hierarchie von der Basis zur am weitesten abgeleiteten Methode aufgerufen.The attributes can be applied at all levels of the type inheritance hierarchy, and each method is called in the hierarchy from the base to the most derived. Durch diesen Mechanismus werden die Komplexität und alle hieraus entstehenden Probleme bei der Implementierung der ISerializable-Schnittstelle vermieden, indem die Verantwortung für die Serialisierung und Deserialisierung der am weitesten abgeleiteten Implementierung übergeben wird.This mechanism avoids the complexity and any resulting issues of implementing the ISerializable interface by giving the responsibility for serialization and deserialization to the most derived implementation. Zusätzlich ermöglicht dieser Mechanismus den Formatierungsprogrammen, das Auffüllen von Feldern und das Abrufen von Daten aus dem Serialisierungsstream zu ignorieren.Additionally, this mechanism allows the formatters to ignore the population of fields and retrieval from the serialization stream. Weitere Informationen und Beispiele zum Steuern der Serialisierung und Deserialisierung erhalten Sie, indem Sie auf einen der oben genannten Links klicken.For details and examples of controlling serialization and deserialization, click any of the previous links.

Wenn Sie einem bestehenden serialisierbaren Typ ein neues Feld hinzufügen, wenden Sie außerdem das OptionalFieldAttribute-Attribut auf das Feld an.In addition, when adding a new field to an existing serializable type, apply the OptionalFieldAttribute attribute to the field. BinaryFormatter und SoapFormatter ignorieren das Fehlen des Felds, wenn ein Stream mit fehlendem neuen Feld verarbeitet wird.The BinaryFormatter and the SoapFormatter ignores the absence of the field when a stream that is missing the new field is processed.

Implementieren der ISerializable-SchnittstelleImplementing the ISerializable interface

Die andere Möglichkeit zum Steuern der Serialisierung besteht in der Implementierung der ISerializable-Schnittstelle für ein Objekt.The other way to control serialization is achieved by implementing the ISerializable interface on an object. Beachten Sie jedoch, dass die Methode im vorhergehenden Abschnitt beim Steuern der Serialisierung Vorrang vor dieser Methode hat.Note, however, that the method in the previous section supersedes this method to control serialization.

Außerdem dürfen Sie keine Standardserialisierung bei einer Klasse verwenden, die mit dem Serializable-Attribut markiert ist und für die auf Klassenebene oder für ihre Konstruktoren deklarative oder imperative Sicherheit gilt.In addition, you should not use default serialization on a class that is marked with the Serializable attribute and has declarative or imperative security at the class level or on its constructors. Stattdessen sollten diese Klassen immer die ISerializable-Schnittstelle implementieren.Instead, these classes should always implement the ISerializable interface.

Die Implementierung von ISerializable umfasst auch die Implementierung der GetObjectData-Methode und eines speziellen Konstruktors, der bei der Deserialisierung des Objekts verwendet wird.Implementing ISerializable involves implementing the GetObjectData method and a special constructor that is used when the object is deserialized. Der folgende Beispielcode zeigt, wie ISerializable für die MyObject-Klasse aus einem vorherigen Abschnitt implementiert wird.The following sample code shows how to implement ISerializable on the MyObject class from a previous section.

[Serializable]
public class MyObject : ISerializable
{
    public int n1;
    public int n2;
    public String str;

    public MyObject()
    {
    }

    protected MyObject(SerializationInfo info, StreamingContext context)
    {
      n1 = info.GetInt32("i");
      n2 = info.GetInt32("j");
      str = info.GetString("k");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("i", n1);
        info.AddValue("j", n2);
        info.AddValue("k", str);
    }
}
<Serializable()>  _
Public Class MyObject
    Implements ISerializable
    Public n1 As Integer
    Public n2 As Integer
    Public str As String

    Public Sub New()
    End Sub

    Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        n1 = info.GetInt32("i")
        n2 = info.GetInt32("j")
        str = info.GetString("k")
    End Sub 'New

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        info.AddValue("i", n1)
        info.AddValue("j", n2)
        info.AddValue("k", str)
    End Sub
End Class

Wenn GetObjectData während der Serialisierung aufgerufen wird, müssen Sie das mit dem Methodenaufruf bereitgestellte SerializationInfo auffüllen.When GetObjectData is called during serialization, you are responsible for populating the SerializationInfo provided with the method call. Fügen Sie die zu serialisierenden Variablen einfach als Name/Wert-Paar hinzu.Add the variables to be serialized as name and value pairs. Beliebiger Text kann als Name verwendet werden.Any text can be used as the name. Sie können frei entscheiden, welche Membervariablen SerializationInfo hinzugefügt werden. Voraussetzung ist jedoch, dass genügend Daten serialisiert werden, um das Objekt während der Deserialisierung wiederherzustellen.You have the freedom to decide which member variables are added to the SerializationInfo, provided that sufficient data is serialized to restore the object during deserialization. Abgeleitete Klassen sollten die GetObjectData-Methode für das Basisobjekt aufrufen, wenn dieses ISerializable implementiert.Derived classes should call the GetObjectData method on the base object if the latter implements ISerializable.

Beachten Sie, dass es durch die Serialisierung anderem Code ermöglicht werden kann, Objektinstanzdaten anzuzeigen oder zu ändern, auf die andernfalls nicht zugegriffen werden .Note that serialization can allow other code to see or modify object instance data that is otherwise inaccessible. Daher ist für Code, der eine Serialisierung ausführt, SecurityPermission unter Angabe des SerializationFormatter-Flags erforderlich.Therefore, code that performs serialization requires the SecurityPermission with the SerializationFormatter flag specified. Entsprechend der Standardrichtlinie bedeutet dies, dass diese Berechtigung nicht für aus dem Internet heruntergeladenen Code oder Code aus einem Intranet erteilt wird. Diese Berechtigung wird nur für Code auf dem lokalen Computer erteilt.Under default policy, this permission is not given to Internet-downloaded or intranet code; only code on the local computer is granted this permission. Die GetObjectData-Methode muss entweder durch Anfordern von SecurityPermission unter Angabe des SerializationFormatter-Flags oder durch Anfordern von anderen Berechtigungen, die insbesondere private Daten schützen, explizit geschützt werden.The GetObjectData method must be explicitly protected either by demanding the SecurityPermission with the SerializationFormatter flag specified or by demanding other permissions that specifically help protect private data.

Wenn in einem privaten Feld vertrauliche Informationen gespeichert werden, sollten Sie die entsprechenden Berechtigungen für GetObjectData anfordern, um die Daten zu schützen.If a private field stores sensitive information, you should demand the appropriate permissions on GetObjectData to protect the data. Berücksichtigen Sie, dass ein Code, dem die Berechtigung SecurityPermission unter Angabe des SerializationFormatter-Flags erteilt wurde, die in privaten Feldern gespeicherten Daten anzeigen und verändern kann.Remember that code that has been granted SecurityPermission with the SerializationFormatter flag specified can view and modify the data stored in private fields. Ein böswilliger Aufrufer, dem die Berechtigung SecurityPermission erteilt wurde, kann Daten, wie etwa ausgeblendete Verzeichnispfade oder erteilte Berechtigungen, anzeigen und die Daten verwenden, um eine Sicherheitslücke auf dem Computer auszunutzen.A malicious caller granted this SecurityPermission can view data such as hidden directory locations or granted permissions and use the data to exploit a security vulnerability on the computer. Eine vollständige Liste der Flags für Sicherheitsberechtigungen, die Sie angeben können, finden Sie unter SecurityPermissionFlag-Enumeration.For a complete list of the security permission flags you can specify, see the SecurityPermissionFlag Enumeration.

Beachten Sie, dass in einer Klasse, der ISerializable hinzugefügt wurde, sowohl GetObjectData als auch der spezielle Konstruktor implementiert werden müssen.It's important to stress that when ISerializable is added to a class you must implement both GetObjectData and the special constructor. Der Compiler gibt eine Warnung aus, wenn GetObjectData fehlt.The compiler warns you if GetObjectData is missing. Wenn jedoch der Konstruktor fehlt, wird keine Warnung ausgegeben, da die Implementierung eines Konstruktors nicht erzwungen werden kann. Wenn außerdem versucht wird, eine Klasse ohne den Konstruktor zu deserialisieren, wird eine Ausnahme ausgelöst.However, because it is impossible to enforce the implementation of a constructor, no warning is provided if the constructor is absent, and an exception is thrown when an attempt is made to deserialize a class without the constructor.

Der aktuelle Entwurf wurde einer SetObjectData-Methode vorgezogen, um potenzielle Sicherheits- und Versionsprobleme zu umgehen.The current design was favored above a SetObjectData method to get around potential security and versioning problems. Eine SetObjectData-Methode muss beispielsweise öffentlich sein, wenn sie als Teil einer Schnittstelle definiert wird. Die Benutzer müssen demnach einen Code schreiben, um zu verhindern, dass die SetObjectData-Methode mehrfach aufgerufen wird.For example, a SetObjectData method must be public if it is defined as part of an interface; thus users must write code to defend against having the SetObjectData method called multiple times. Andernfalls kann eine bösartige Anwendung, die die SetObjectData-Methode für ein Objekt während der Ausführung eines Vorgangs aufruft, Probleme verursachen.Otherwise, a malicious application that calls the SetObjectData method on an object in the process of executing an operation can cause potential problems.

Während der Deserialisierung wird SerializationInfo an die Klasse übergeben, die den für diesen Zweck bereitgestellten Konstruktor verwendet.During deserialization, SerializationInfo is passed to the class using the constructor provided for this purpose. Die für den Konstruktor geltenden Sichtbarkeitseinschränkungen werden bei der Deserialisierung des Objekts ignoriert, d. h., Sie können die Klasse als öffentlich, geschützt, intern oder privat markieren.Any visibility constraints placed on the constructor are ignored when the object is deserialized; so you can mark the class as public, protected, internal, or private. Der Konstruktor sollte jedoch als geschützt markiert werden, sofern die Klasse nicht versiegelt ist. Ist dies der Fall, sollte der Konstruktor als privat markiert werdenHowever, it is a best practice to make the constructor protected unless the class is sealed, in which case the constructor should be marked private. Der Konstruktor sollte außerdem eine gründliche Validierung der Eingaben durchführen.The constructor should also perform thorough input validation. Um Missbrauch durch bösartigen Code zu verhindern, muss der Konstruktor dieselben Sicherheitsüberprüfungen und Berechtigungen erzwingen, die zum Abrufen einer Instanz der Klasse durch einen anderen Konstruktor erforderlich sind.To avoid misuse by malicious code, the constructor should enforce the same security checks and permissions required to obtain an instance of the class using any other constructor. Wenn Sie diese Empfehlung nicht umsetzen, kann bösartiger Code ein Objekt im Voraus serialisieren. Der Code kann dann mit der Berechtigung SecurityPermission unter Angabe des SerializationFormatter-Flags die Steuerung übernehmen und das Objekt auf einem Clientcomputer deserialisieren, wobei alle Sicherheitseinstellungen umgangen werden, die bei einer standardmäßigen Instanzerstellung mit einem öffentlichen Konstruktor gelten.If you do not follow this recommendation, malicious code can preserialize an object, obtain control with the SecurityPermission with the SerializationFormatter flag specified and deserialize the object on a client computer bypassing any security that would have been applied during standard instance construction using a public constructor.

Um den Zustand des Objekts wiederherzustellen, müssen nur die Werte der Variablen aus SerializationInfo mithilfe der bei der Serialisierung verwendeten Namen abgerufen werden.To restore the state of the object, simply retrieve the values of the variables from SerializationInfo using the names used during serialization. Wenn die Basisklasse ISerializable implementiert, sollte der Basiskonstruktor aufgerufen werden, damit das Basisobjekt seine Variablen wiederherstellen kann.If the base class implements ISerializable, the base constructor should be called to allow the base object to restore its variables.

Wenn Sie von einer Klasse, die ISerializable implementiert, eine neue Klasse ableiten, muss die abgeleitete Klasse sowohl den Konstruktor als auch die GetObjectData-Methode implementieren, wenn sie über zu serialisierende Variablen verfügt.When you derive a new class from one that implements ISerializable, the derived class must implement both the constructor as well as the GetObjectData method if it has variables that need to be serialized. Im folgenden Codebeispiel wird dieser Vorgang unter Verwendung der bereits zuvor gezeigten MyObject-Klasse veranschaulicht.The following code example shows how this is done using the MyObject class shown previously.

[Serializable]
public class ObjectTwo : MyObject
{
    public int num;

    public ObjectTwo()
      : base()
    {
    }

    protected ObjectTwo(SerializationInfo si, StreamingContext context)
      : base(si, context)
    {
        num = si.GetInt32("num");
    }

    [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo si, StreamingContext context)
    {
        base.GetObjectData(si,context);
        si.AddValue("num", num);
    }
}
<Serializable()>  _
Public Class ObjectTwo
    Inherits MyObject
    Public num As Integer

    Public Sub New()

    End Sub

    Protected Sub New(ByVal si As SerializationInfo, _
    ByVal context As StreamingContext)
        MyBase.New(si, context)
        num = si.GetInt32("num")
    End Sub

    <SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter := True)> _
    Public Overrides Sub GetObjectData(ByVal si As SerializationInfo, ByVal context As StreamingContext)
        MyBase.GetObjectData(si, context)
        si.AddValue("num", num)
    End Sub
End Class

Vergessen Sie nicht, die Basisklasse im Deserialisierungskonstruktor aufzurufen.Don't forget to call the base class in the deserialization constructor. Andernfalls wird der Konstruktor für die Basisklasse nicht aufgerufen, und das Objekt wird nach der Deserialisierung nicht vollständig erstellt.If this isn't done, the constructor on the base class is never called, and the object is not fully constructed after deserialization.

Objekte werden von innen nach außen rekonstruiert. Dies bedeutet, dass das Aufrufen von Methoden während der Deserialisierung unerwünschte Nebeneffekte haben kann, wenn sich die aufgerufenen Methoden auf Objektverweise beziehen, die zum Zeitpunkt des Aufrufs noch nicht deserialisiert worden sind.Objects are reconstructed from the inside out; and calling methods during deserialization can have undesirable side effects, because the methods called might refer to object references that have not been deserialized by the time the call is made. Wenn die deserialisierte Klasse IDeserializationCallback implementiert, wird die OnDeserialization-Methode automatisch aufgerufen, sobald die gesamte Objektgrafik deserialisiert wurde.If the class being deserialized implements the IDeserializationCallback, the OnDeserialization method is automatically called when the entire object graph has been deserialized. Zu diesem Zeitpunkt sind auch die untergeordneten Objekte, auf die verwiesen wurde, vollständig wiederhergestellt.At this point, all the child objects referenced have been fully restored. Eine Hashtabelle ist ein typisches Beispiel für eine Klasse, die ohne Verwendung des Ereignislisteners schwer zu deserialisieren ist.A hash table is a typical example of a class that is difficult to deserialize without using the event listener. Es ist einfach, die Schlüssel- und Wert-Paare während der Deserialisierung abzurufen. Es kann jedoch problematisch sein, diese Objekte dann wieder der Hashtabelle hinzuzufügen, da nicht gewährleistet ist, dass die von der Hashtabelle abgeleiteten Klassen deserialisiert wurden.It is easy to retrieve the key and value pairs during deserialization, but adding these objects back to the hash table can cause problems, because there is no guarantee that classes that derived from the hash table have been deserialized. Daher wird davon abgeraten, zu diesem Zeitpunkt Methoden für Hashtabellen aufzurufen.Calling methods on a hash table at this stage is therefore not advisable.

Siehe auchSee also