100 Stimmen

DataContractSerializer ruft meinen Konstruktor nicht auf?

Ich habe gerade etwas Verrücktes bemerkt, von dem ich annahm, dass es völlig unmöglich ist: beim Deserialisieren eines Objekts wird die DataContractSerializer ruft den Konstruktor nicht auf !

Nehmen Sie zum Beispiel diese Klasse:

[DataContract]
public class Book
{
    public Book()
    { // breakpoint here
    }

    [DataMember(Order = 0)]
    public string Title { get; set; }
    [DataMember(Order = 1)]
    public string Author { get; set; }
    [DataMember(Order = 2)]
    public string Summary { get; set; }
}

Wenn ich ein Objekt dieser Klasse deserialisiere, wird der Haltepunkt nicht getroffen. Ich habe absolut keine Ahnung, wie das möglich ist, da dies der einzige Konstruktor für dieses Objekt ist!

Ich nahm an, dass vielleicht ein zusätzlicher Konstruktor vom Compiler erzeugt wurde, weil die DataContract Attribut, aber ich konnte es durch Nachdenken nicht finden...

Also, was ich gerne wissen würde, ist dies: wie könnte eine Instanz meiner Klasse erstellt werden, ohne den Konstruktor aufgerufen wird?

HINWEIS: Ich weiß, dass ich die OnDeserializing Attribut, um mein Objekt zu initialisieren, wenn die Deserialisierung beginnt, ist dies nicht das Thema meiner Frage.

135voto

Marc Gravell Punkte 970173

DataContractSerializer (wie BinaryFormatter ) verwendet nicht cualquier Konstrukteur. Er erzeugt das Objekt als leeren Speicher.

Zum Beispiel:

    Type type = typeof(Customer);
    object obj = System.Runtime.Serialization.
        FormatterServices.GetUninitializedObject(type);

Es wird davon ausgegangen, dass der Deserialisierungsprozess (oder ggf. Callbacks) sie vollständig initialisieren wird.

3voto

BasvdL Punkte 104

Es gibt einige Szenarien, die ohne dieses Verhalten nicht möglich wären. Denken Sie an das Folgende:

1) Sie haben ein Objekt, das einen Konstruktor hat, der die neue Instanz in einen "initialisierten" Zustand versetzt. Dann werden einige Methoden für diese Instanz aufgerufen, die sie in einen "verarbeiteten" Zustand versetzen. Sie möchten keine neuen Objekte mit dem "verarbeiteten" Zustand erstellen, aber Sie möchten die Instanz trotzdem serialisieren/deserialisieren.

2) Sie haben eine Klasse mit einem privaten Konstruktor und einigen statischen Eigenschaften erstellt, um einen kleinen Satz von zulässigen Konstruktorparametern zu steuern. Jetzt können Sie sie noch serialisieren / deserialisieren.

XmlSerializer hat das Verhalten, das Sie erwartet. Ich habe einige Probleme mit dem XmlSerializer gehabt, weil es einen Standardkonstruktor benötigt. Im Zusammenhang mit, dass, manchmal ist es sinnvoll, private Eigenschaft Setter haben. Aber der XmlSerializer braucht auch öffentliche Getter und Setter auf Eigenschaften, um zu serialisieren / deserialisieren.

Ich stelle mir das Verhalten von DataContractSerializer / BinaryFormatter so vor, dass der Zustand einer Instanz während der Serialisierung angehalten und während der Deserialisierung wieder aufgenommen wird. Mit anderen Worten, die Instanzen werden nicht "konstruiert", sondern in einem früheren Zustand "wiederhergestellt".

Wie Sie bereits erwähnt haben, ermöglicht das [OnDeserializing]-Attribut, nicht serialisierte Daten synchron zu halten.

1voto

Bruno Martinez Punkte 2697

Übrigens, Sie peut den Konstruktor explizit aus einer [OnDeserializing]-Methode aufrufen:

[OnDeserializing]
public void OnDeserializing(StreamingContext context)
{
    this.GetType().GetConstructor(System.Array.Empty<Type>()).Invoke(this, null);
}

0voto

Temala Ridha Punkte 84

Verwenden Sie das Attribut [OnDeserialized], um Ihre Eigenschaften zu initialisieren.

// This method is called after the object
// is completely deserialized. Use it instead of the
// constructror.
[OnDeserialized]
void OnDeserialized(StreamingContext context)
{
    fullName = firstName + " " + lastName;
}

Bitte beachten Sie die Microsoft-Leitlinien: https://docs.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines

0voto

hookah kid Punkte 1

In meinem Fall wollte ich ein Objekt zur Verwendung in einer Sperrklausel erstellen. Ich habe versucht, IDeserializationCallback zu implementieren (hat nicht funktioniert, weil Callback nur läuft nach Eigenschaften zugewiesen wurden), [OnDeserialized] (funktionierte nicht, gleicher Grund) und ISerializable (funktionierte nicht, weil die Klasse mit dem [DataContractAttribute] dekoriert ist).

Meine Abhilfe bestand darin, das Feld zu initialisieren, bevor es mit Interlocked.CompareExchange verwendet wird. Ein bisschen unnötige Arbeit wird getan, aber zumindest jetzt mein Feld wird initialisiert, wenn ein DataContractSerializer es erstellt.

Interlocked.CompareExchange(ref _sync, new object(), null);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X