ПРИМЕЧАНИЕ: Этот вопрос немного изменился, когда я узнал больше о проблеме, поэтому, пожалуйста, прочитайте ее полностью. Я решил оставить его в первоначальном виде, так как он лучше описывает, как проблема была обнаружена и в конечном итоге решена.
Вернемся в самые темные глубины истории нашего проекта, когда мы действительно не понимали C # или CLR так, как могли бы, мы создали тип, назовем его MyType
. Мы создали этот тип как class
, ссылочный тип.
Однако стало очевидно, что MyType
должен быть struct
, типом значения, поэтому мы внесли некоторые изменения, чтобы сделать это, и все было хорошо, пока однажды мы не попытались десериализовать некоторые данные, содержащие коллекцию MyType
значения. Ну, не совсем, потому что когда это был ссылочный тип, эта коллекция была коллекцией ссылок. Теперь, когда десериализуется, коллекция десериализируется нормально, используя конструктор по умолчанию MyType
, потом, когда десериализуются фактические ссылки, они теряются, оставляя нам коллекцию пустых значений.
Итак, мы подумали: «давайте сопоставим тип с ссылочным типом, MyTypeRef
при загрузке, чтобы ссылки правильно разрешались, а затем преобразовали обратно в наш реальный тип для использования во время выполнения и повторной сериализации». Так мы и сделали (используя наше собственное связующее), но, увы, это не сработало, потому что теперь мы получаем ошибку, которая говорит нам, что MyTypeRef[]
нельзя преобразовать в MyType[]
, даже если у нас есть неявное преобразование между MyTypeRef
и MyType
.
Итак, мы застряли. Как получить коллекцию, сериализованную как коллекцию ссылочного типа MyType
, чтобы десериализовать ее как коллекцию типа значения MyType
?
Обновление
Некоторое исследование (см. Комментарии и код ниже) показало, что реальная проблема вызвана неизменным характером нового MyType
и его использования ISerializable
для сериализации. Я до сих пор не понимаю, почему это так, но если я использую private
установить методы доступа вместо ISerializable
, новый MyType
загрузит старый (обратите внимание, что интерфейс ISerializable
вызывается, если я использую это, но коллекция содержит только значения по умолчанию).
Какой-то код
// Use a List<T> in a class that also implements ISerializable and
// save an instance of that class with a BinaryFormatter (code omitted)
// Save with this one.
[Serializable]
public class MyType
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
}
// Load with this one.
[Serializable]
public class MyType : ISerializable
{
private string test;
public string Test
{
get { return this.test; }
set { this.test = value; }
}
public MyType()
{
}
public MyType(SerializationInfo info, StreamingContext context)
{
info.AddValue("test", this.test);
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.test = info.GetString("test");
}
}
Обратите внимание, что при загрузке все элементы равны нулю. Удалите ISerializable
из второго определения и загрузите и все работает. Измените class
на struct
в коде загрузки, и то же поведение будет видно. Похоже, что коллекция будет успешно десериализована только с использованием установленных методов доступа.
Обновление два
Итак, я нашел проблему (см. Мой ответ ниже), но я сомневаюсь, что кто-то знал бы ответ, прочитав мой вопрос. Я упустил важную деталь, которую я даже не осознавал, которая была важна в то время. Мои искренние извинения тем, кто пытался помочь.
Загруженная коллекция, содержащая MyType
, немедленно копируется в другую коллекцию во время GetObjectData
. Ответ ниже объясняет, почему это оказывается важным. Вот некоторый дополнительный пример кода к приведенному выше, который должен предоставить полный пример:
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
this.myTypeCollection = new List<MyType>();
var loadedCollection = (List<MyType>)info.GetValue(
"myTypeCollection",
typeof(List<MyType>));
this.myTypeCollection.AddRange(loadedCollection);
}