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