Эта ситуация является причиной для метода FormatterServices.GetUninitializedObject
. Общая идея заключается в том, что если у вас есть объекты A и B, которые ссылаются друг на друга в их SerializationInfo
, вы можете десериализовать их следующим образом:
(Для целей этого объяснения (SI,SC)
относится к конструктору десериализации типа, то есть к тому, который принимает SerializationInfo
и StreamingContext
.)
- Выберите один объект для десериализации первым. Неважно, какой вы выбираете, если вы не выбираете тот, который является типом значения. Допустим, вы выбрали A.
- Вызовите
GetUninitializedObject
, выделите (но не инициализируйте) экземпляр типа A, потому что вы еще не готовы вызвать его конструктор (SI,SC)
.
- Создайте B обычным способом, то есть создайте объект
SerializationInfo
(который будет включать ссылку на теперь уже частично десериализованный A) и передайте его конструктору (SI,SC)
B.
- Теперь у вас есть все зависимости, необходимые для инициализации вашего выделенного объекта A. Создайте объект
SerializationInfo
и вызовите конструктор (SI,SC)
. Вы можете вызвать конструктор в существующем экземпляре через отражение.
Метод GetUninitializedObject
является чистой магией CLR - он создает экземпляр, даже не вызывая конструктор для его инициализации. Это в основном устанавливает все поля в ноль / ноль.
По этой причине вам не рекомендуется использовать какой-либо элемент дочернего объекта в конструкторе (SI,SC)
- дочерний объект может быть выделен, но еще не инициализирован в этой точке. Это также является причиной интерфейса IDeserializationCallback
, который дает вам возможность использовать ваши дочерние объекты после того, как гарантированно будет выполнена вся инициализация объекта и до возвращения десериализованного графа объектов.
Класс ObjectManager может сделать все это (и другие типы исправлений) за вас. Тем не менее, я всегда считал, что он недостаточно документирован, учитывая сложность десериализации, поэтому я никогда не тратил время на то, чтобы понять, как правильно его использовать. Для выполнения шага 4 требуется больше магии, используется некоторое внутреннее отражение в CLR, оптимизированное для более быстрого вызова конструктора (SI,SC)
(я рассчитал его примерно вдвое быстрее, чем общедоступный способ).
Наконец, существуют графы объектов, включающие циклы, которые невозможно десериализовать. Один пример - когда у вас есть цикл из двух IObjectReference
экземпляров (я протестировал BinaryFormatter
на этом, и он выдает исключение). Я подозреваю, что если у вас есть цикл , включающий только типы значений в штучной упаковке .