ありがとうございました!ISerializableを使用したList <IMyInterface>のシリアライズ
私は現場に展開されている製品の新しいバージョンで作業しています。古いソフトウェアのファイルを脱直列化する機能を維持する必要があります。ここで
は不自然な例です:
私は、彼らがアクセスする必要がシリアライズされたファイルで既存の顧客基盤を持っています。この質問の目的のために、彼らはそれにキリンのリストを持つ "動物園"ファイルを持っています。
[Serializable]
public class Giraffe
: ISerializable
{
public int Age { get; private set; }
public Giraffe(int age)
{
Age = age;
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Age", Age);
}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
private Giraffe(SerializationInfo info, StreamingContext context)
{
Age = info.GetInt32("Age");
}
}
今、私たちは私たちの「動物園」ソフトウェアの新バージョンを展開している、と我々はキリン以外anaimals他をサポートしようとしている、私たちは、これはそもそも行っているはずですが、時間が制約に起因して、我々は持っていましたキリンのみの機能セットで1.0をリリースする。
public interface IAnimal
{
int Age { get; }
}
[Serializable]
public class Animal
: IAnimal,
ISerializable
{
public int Age { get; private set; }
public Animal (int age)
{
Age = age;
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Age", Age);
}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
private Animal(SerializationInfo info, StreamingContext context)
{
Age = info.GetInt32("Age");
}
}
私はこの問題は、私は、それがストレージだと私の動物園のクラスは、リストを使用していないことwan'tことです動物
public class LegacyZooSerializationBinder
: SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
if (typeName.StartsWith("Test.Giraffe"))
return typeof(Animal);
else if (typeName.StartsWith("System.Collections.Generic.List`1[[Test.Giraffe"))
return typeof(List<Animal>);
else return null;
}
}
としてデシリアライズさ古いキリンを持っているカスタムserializationBinderを使用していますリスト。私はこれを2つの理由から、将来の拡張性のために、また、単体テストのためのものをより簡単に模倣できるようにしたいと思っています。
新しいIAnimalリストを逆シリアル化することは問題ありません。問題は古いスタイルのアイテムを逆シリアル化したいときです。バインダは正しい型を返し、正しい逆シリアル化コンストラクタが呼び出され、すべてが正常に見えますが、Listは実際には空の項目でいっぱいです。 IDeserializationCallback.OnDeserializationコールバックが呼び出されると、リストは正しいです。シリアライゼーションフレームワークがすべて同じものを見つけようとしているように見えるので、IEnumerable.ConvertAll <>()を呼び出すだけでは、簡単には呼び出せません.ConvertAllは新しいリストを作成します。
私はそれを今のところ働いていますが、私は誰もがこれをきれいにするのを助けてくれることを願っています。ここでは、それはそれを行うために必要なものである:ここでは
[Serializable]
public class Zoo
: ISerializable,
IDeserializationCallback
{
List<IAnimal> m_List = null;
List<Giraffe> m_LegacyList = null; //Just so that we can save an old-style zoo
//Temp copy of the list
List<Animal> m_List_Deserialization_Temp_Copy = null;
public Zoo(bool legacy)
{
m_List = new List<IAnimal>();
if (legacy)
{
//Create an old style zoo, just for the example
m_LegacyList = new List<Giraffe>();
m_LegacyList.Add(new Giraffe(0));
m_LegacyList.Add(new Giraffe(1));
}
else
{
m_List.Add(new Animal(0));
m_List.Add(new Animal(1));
}
}
public List<IAnimal> List
{
get { return m_List; }
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if(m_LegacyList != null) //Save as an old style list if we have old data
info.AddValue("list", m_LegacyList);
else
info.AddValue("list", m_List);
}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
private Zoo(SerializationInfo info, StreamingContext context)
{
try
{
//New style
m_List = (List<IAnimal>)info.GetValue("list", typeof(List<IAnimal>));
}
catch (InvalidCastException)
{
//Old style
//Put it in a temp list, until the OnDeserialization callback is called, this will be a list, full of null items!
m_List_Deserialization_Temp_Copy = (List<Animal>)info.GetValue("list", typeof(List<Animal>));
}
}
void IDeserializationCallback.OnDeserialization(object sender)
{
if (m_List_Deserialization_Temp_Copy != null)
{
m_List = new List<IAnimal>();
//This works because IEnumerable<Animal> is covariant to IEnumerable<IAnimal>
m_List.AddRange(m_List_Deserialization_Temp_Copy);
}
}
}
は両方のケースのためのシリアライズとデシリアライズを示し、基本的なテストコンソールアプリです:
static void Main(string[] args)
{
{
var item = new Zoo(false);
var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, item);
stream.Position = 0;
formatter.Binder = new LegacyZooSerializationBinder();
var deserialized = (Zoo)formatter.Deserialize(stream);
Debug.Assert(deserialized.List.Count == 2);
Debug.Assert(deserialized.List[0] != null);
Debug.Assert(deserialized.List[0].Age == 0);
Debug.Assert(deserialized.List[1] != null);
Debug.Assert(deserialized.List[1].Age == 1);
Console.WriteLine("New-style Zoo serialization OK.");
}
{
var item = new Zoo(true);
var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, item);
stream.Position = 0;
formatter.Binder = new LegacyZooSerializationBinder();
var deserialized = (Zoo)formatter.Deserialize(stream);
Debug.Assert(deserialized.List.Count == 2);
Debug.Assert(deserialized.List[0] != null);
Debug.Assert(deserialized.List[0].Age == 0);
Debug.Assert(deserialized.List[1] != null);
Debug.Assert(deserialized.List[1].Age == 1);
Console.WriteLine("Old-style Zoo serialization OK.");
}
Console.ReadKey();
}
任意の提案をいただければ幸いです。私はこのタイプのもので良い資源を見つけるのに苦労しています。ありがとう!
+1「キリンのみの機能セット」です。私はそれを楽しんだ。 –
あなたは永遠にキリンの特別なケースを維持しなければならないという頭痛を救うために、インストール時に一度だけキリンから動物への変換を行うことを考えましたか? –
クリス、私はワンショット変換を考えていませんでした。それは明らかにコード内のものをかなりきれいにするだろう。私は、特にバックエンドの変更であるため、ユーザーには見えないようにしたいと考えています。他のすべてが失敗した場合、それは本当に良い解決策になる可能性があります。 – Brandon