ISerializable и обратной совместимости
Я должен работать со старым приложением, которое использовало binaryFormatter для сериализации данных приложения в filestream (скажем, в файле с именем "data.олдформат") без какой-либо оптимизации основной класс был отмечен атрибутом
<serializable()>public MainClass
.......
end class
И код сериализации
dim b as new binaryformatter
b.serialize(mystream,mymainclass)
В попытке оптимизировать процесс сериализации / десериализации я просто заставил класс реализовать интерфейс ISerializable и написал некоторые оптимизированные процедуры сериализации
<serializable()>public MainClass
implements ISerializable
.......
end class
Оптимизация работает очень хорошо, но я должен найти способ восстановить данные внутри старых файлов для обратной совместимости.
Как я могу это сделать??
Пьерлуиджи
5 ответов:
Stmax имеет отличный ответ, однако я бы реализовал его так, что использует
SerializationEntry.GetEnumerator()
вместоtry/catch
. Этот способ чище и значительно быстрее.public MainClass(SerializationInfo info, StreamingContext context) { int version = 0; foreach (SerializationEntry s in info) { if (s.Name == "version") { version = (int)s.Value; break; } } switch (version) { case 0: // deserialize "old format" break; case 1: // deserialize "new format, version 1" break; default: throw new NotSupportedException("version " + version + " is not supported."); } }
Я бы предпочел использовать версию LINQ .FirstOrDefault (), но SerializationInfo не реализует IEnumerable-в face, как ни странно, он даже не реализует старый интерфейс IEnumerable.
Поскольку вы уже реализовали интерфейс ISerializable, вы, вероятно, также уже добавили требуемый конструктор:
public MainClass(SerializationInfo info, StreamingContext context) {}
Вы можете использовать объект info, переданный конструктору, для извлечения данных из сериализованного файла. по умолчанию (т. е. при отсутствии реализации ISerializable) имена полей используются в качестве идентификаторов во время сериализации. поэтому, если ваш старый класс имел поле "int x", вы можете десериализовать его, используя:
this.x = info.GetInt32("x");
Для более новых версий я обычно добавляю запись "version" во время сериализации, например:
public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("version", 1); info.AddValue("othervalues", ...); }
Во время десериализации вы можете проверить эту запись версии и десериализовать соответственно:
public MainClass(SerializationInfo info, StreamingContext context) { int version; try { version = info.GetInt32("version"); } catch { version = 0; } switch (version) { case 0: // deserialize "old format" break; case 1: // deserialize "new format, version 1" break; default: throw new NotSupportedException("version " + version + " is not supported."); } }
Я не компилировал этот код, возможно, он содержит опечатки.
Надеюсь, это поможет.
Просто попробуйте то же самое, что вы делали до сих пор
BinaryFormatter b = new BinaryFormatter(); MainClass a = b.DeSerialize(mystream) as MainClass;
Реализация ISerializable не изменила ваш исходный класс, в основном вы просто добавили некоторые методы
При сериализации объектов добавьте дополнительное поле версии (это не должно добавлять слишком много накладных расходов). Затем в методе GetObjectData попытайтесь сначала получить поле version и на основе того, существует ли это или нет (поймав SerializationException) десериализовать старый способ или новый способ. Старый способ будет просто сериализовать все данные, так что вы должны просто быть в состоянии вызвать Get... для всех полей.