Ответ 1
Есть две вещи, вызывающие конструктор (или, по крайней мере, должны делать).
Один из них заключается в том, чтобы выделить определенный объем памяти для объекта и сделать все домашнее хозяйство необходимым для того, чтобы он был объектом для остальной части мира .NET(обратите внимание на определенную величину ручной работы в этом объяснении).
Другим является помещение объекта в допустимое начальное состояние, возможно, на основе параметров - это то, что будет делать фактический код в конструкторе.
Дезициализация делает то же самое, что и первый шаг, вызывая FormatterServices.GetUninitializedObject
, а затем делает то же самое, что и второй шаг, устанавливая значения полей равными тем, которые были записаны во время сериализации (что может потребовать десериализация других объектов, подлежащих указанным значениям).
Теперь состояние, в котором десериализация помещается в объект, может не соответствовать тому, что может быть любым конструктором. В лучшем случае это будет расточительно (все значения, заданные конструктором, будут перезаписаны), а в худшем случае это может быть опасно (конструктор имеет некоторый побочный эффект). Это также может быть просто невозможно (только конструктор - это тот, который принимает параметры - сериализация не имеет способа узнать, какие аргументы использовать).
Вы можете рассматривать его как особый тип конструктора, который используется только при десериализации (пуристы OO будут - и должны - содрогаться от идеи конструктора, который не строится, я имею в виду это только как аналог, если вы знаете С++ думает о том, как переопределение new
работает до тех пор, пока идет память, и у вас есть еще лучшая аналогия, хотя все еще просто аналогия).
Теперь это может быть проблемой в некоторых случаях - возможно, у нас есть поля readonly
, которые могут быть заданы только конструктором, или, возможно, у нас есть побочные эффекты, которые мы хотим выполнить.
Решение обоих заключается в переопределении поведения сериализации с помощью ISerializable
. Это будет сериализоваться на основе вызова ISerializable.GetObjectData
, а затем вызовет конкретный конструктор с полями SerializationInfo
и StreamingContext
для десериализации (указанный конструктор может даже быть частным - это означает, что большинство других кодов даже не увидит его). Следовательно, если мы можем десериализовать поля readonly
и иметь любые побочные эффекты, которые мы хотим (мы также можем делать все возможное, чтобы контролировать то, что сериализовано и как).
Если мы просто заботимся о том, чтобы какой-то побочный эффект произошел при десериализации, которая произошла при построении, мы можем реализовать IDeserializationCallback
, и мы получим IDeserializationCallback.OnDeserialization
, когда будет завершена десериализация.
Что касается других вещей, которые делают то же самое, что и в этом, в .NET существуют другие формы сериализации, но все, что я знаю. Можно вызвать FormatterServices.GetUninitializedObject
самостоятельно, но запретить случай, когда у вас есть сильная гарантия того, что последующий код поместит объект в правильное состояние (то есть именно такую ситуацию, в которой вы находитесь, при десериализации объекта из данных, созданных сериализацией такой же тип объекта), делая это чревато и хороший способ создать очень трудную диагностику ошибок.