100

I just realized something crazy, which I assumed to be completely impossible : when deserializing an object, the DataContractSerializer doesn't call the constructor !

Take this class, for instance :

[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; }
}

When I deserialize an object of that class, the breakpoint is not hit. I have absolutely no idea how it is possible, since it is the only constructor for this object !

I assumed that perhaps an additional constructor was generated by the compiler because of the DataContract attribute, but I couldn't find it through reflection...

So, what I'd like to know is this : how could an instance of my class be created without the constructor being called ??

NOTE: I know that I can use the OnDeserializing attribute to initialize my object when deserialization begins, this is not the subject of my question.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 3
    or the "OnDeserialized", when the object is done deserializing, to fill in the missing fields. – marc_s Jul 02 '09 at 21:21
  • 1
    This question crossed my mind too: http://stackoverflow.com/questions/178645/how-does-wcf-deserialization-instantiate-objects-without-calling-a-constructor – Drew Noakes Aug 24 '09 at 19:01

5 Answers5

135

DataContractSerializer (like BinaryFormatter) doesn't use any constructor. It creates the object as empty memory.

For example:

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

The assumption is that the deserialization process (or callbacks if necessary) will fully initialize it.

Carsten
  • 11,287
  • 7
  • 39
  • 62
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    The constructor should not be called at all when deserializing. If it was called, then 1) How about the resources you have created in constructor? They will leak! 2) You are initializing the object twice, once in constructor and once by the deserialized values. – Dudu Feb 13 '11 at 03:02
  • 2
    @Dodu a: that will not cause a leak b: that (calling the ctor) is how a lot of serializers work; either way is generally fine as log as the behaviour is understood and designer accordingy – Marc Gravell Feb 13 '11 at 08:39
  • 11
    I am just not sure why this functionality would ever be desired. Constructors are for state initialization. Bypassing that just serves no purpose at all. The no-argument constructor should be allowed to setup state whether or not it is about to be deserialized. If they wanted a specific optimization, there is the StreamingContext constructor. – Mranz Dec 05 '11 at 16:15
  • 3
    @Mranz deserialization could also be described as state initialisation, and simply: not all types have parameterless constructors. In my own serializer, I make this optional and off by default (opt-in only) – Marc Gravell Dec 05 '11 at 17:43
  • I can see why this *might* be desired. You are deserializing an object. The serialized object should be a complete representation. – Randy James Jul 15 '13 at 19:51
  • @MarcGravell Is there any way around this? I need my object properties that are not decorated with [Datamember] to be initialized with default values. Currently they are being set to null because the initialization should happen in the constructor which is not being called. – erotavlas May 01 '18 at 16:54
  • @erotavlas it sounds like you want [`[OnDeserializing]`](https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializingattribute.aspx) or [`[OnDeserialized]`](https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ondeserializedattribute.aspx) – Marc Gravell May 01 '18 at 20:08
  • Note however that `DataContractSerializer` does call the constructor for [collections](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/collection-types-in-data-contracts). – dbc Dec 13 '18 at 01:07
  • It also calls the constructor if you are using .NET Core. – Jonathan Allen Jan 10 '19 at 09:09
  • This is just plain WRONG! It makes it IMPOSSIBLE to initialize readonly properties that are not serialized. Not only that, the behavior is INCONSISTENT because it does call the constructor of serialized collection classes. – Dan Sorak Jun 25 '20 at 19:55
  • @Dan the kind of code that would usually be using this API usually doesn't care much for `readonly` - that can be ignored too, at least for instance members (the runtime now enforces this for static fields, due to JIT optimisations on static fields that would become invalid if it were allowed) – Marc Gravell Jun 25 '20 at 20:45
3

There are some scenario's that wouldn’t be possible without this behavior. Think of the following:

1) You have an object that has one constructor that sets the new instance to an "initialized" state. Then some methods are called on that instance, that bring it in a "processed" state. You don’t want to create new objects having the "processed" state, but you still want de serialize / deserialize the instance.

2) You created a class with a private constructor and some static properties to control a small set of allowed constructor parameters. Now you can still serialize / deserialize them.

XmlSerializer has the behavior you expected. I have had a some problems with the XmlSerializer because it DOES need a default constructor. Related to that, sometimes it makes sense to have private property setters. But the XmlSerializer also needs public getter and setter on properties in order to serialize / deserialize.

I think of the DataContractSerializer / BinaryFormatter behavior like suspending the state of an instance during serialization and resuming during deserialization. In other words, the instances are not “constructed” but “restored” to an earlier state.

As you already mentioned, the [OnDeserializing] attribute makes it possible to keep non serialized data in sync.

BasvdL
  • 104
  • 2
  • I would not say that it "suspend the state of an instance" because I just hit a problem where my DataMember was using INotifyPropertyChanged and while the constructor is not called, it will trigger the NotifyPropertyChanged so the behavior will not be suspended. – ForceMagic Dec 11 '13 at 21:31
1

FWIW, you can call the constructor explicitly from a [OnDeserializing] method:

[OnDeserializing]
public void OnDeserializing(StreamingContext context)
{
    this.GetType().GetConstructor(System.Array.Empty<Type>()).Invoke(this, null);
}
Bruno Martinez
  • 2,850
  • 2
  • 39
  • 47
0

Use [OnDeserialized] attribute to initialise your properties.

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

Please refer to microsoft guid-lines: https://learn.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines

Temala Ridha
  • 84
  • 1
  • 8
0

In my case, i wanted to create an object to use in a lock-clause. I tried implementing IDeserializationCallback (didn't work because callback only runs after properties have been assigned), [OnDeserialized] (didn't work, same previous reason), and ISerializable (didn't work because the class is decorated with the [DataContractAttribute]).

My workaround was to initialize the field before it's used using Interlocked.CompareExchange. A bit of unnecessary work gets done, but at least now my field gets initialized when a DataContractSerializer creates it.

Interlocked.CompareExchange(ref _sync, new object(), null);
hookah kid
  • 11
  • 3