There are two things calling a constructor does (or at least should do).
One is to set aside a certain amount of memory for the object and does all the housekeeping necessary for it to be an object to the rest of the .NET world (note certain amount of handwaving in this explanation).
The other is to put the object into a valid initial state, perhaps based on parameters - this is what the actual code in the constructor will do.
Deserialisation does much the same thing as the first step by calling FormatterServices.GetUninitializedObject
, and then does much the same thing as the second step by setting the values for fields to be equivalent to those that were recorded during serialisation (which may require deserialising other objects to be said values).
Now, the state that deserialisation is putting the object into may not correspond to that possible by any constructor. At best it will be wasteful (all values set by the constructor will be overwritten) and at worse it could be dangerous (constructor has some side-effect). It could also just be impossible (only constructor is one that takes parameters - serialisation has no way of knowing what arguments to use).
You could look at it as a special sort of constructor only used by deserialisation (OO purists will - and should - shudder at the idea of a constructor that doesn't construct, I mean this as an analogy only, if you know C++ think of the way overriding new
works as far as memory goes and you've an even better analogy, though still just an analogy).
Now, this can be a problem in some cases - maybe we have readonly
fields that can only be set by a constructor, or maybe we have side-effects that we want to happen.
A solution to both is to override serialisation behaviour with ISerializable
. This will serialise based on a call to ISerializable.GetObjectData
and then call a particular constructor with SerializationInfo
and StreamingContext
fields to deserialise (said constructor can even be private - meaning most other code won't even see it). Hence if we can deserialise readonly
fields and have any side-effects we want (we can also do all manner of things to control just what is serialised and how).
If we just care about ensuring some side-effect happens on deserialisation that would happen on construction, we can implement IDeserializationCallback
and we will have IDeserializationCallback.OnDeserialization
called when deserialisation is complete.
As for other things that do the same thing as this, there are other forms of serialisation in .NET but that's all I know of. It is possible to call FormatterServices.GetUninitializedObject
yourself but barring a case where you have a strong guarantee that subsequent code will put the object produced into a valid state (i.e. precisely the sort of situation you are in when deserialising an object from data produced by serialising the same sort of object) doing such is fraught and a good way to produce a really hard to diagnose bug.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…