繁体   English   中英

列表序列化 <IMyInterface> 使用ISerializable

[英]Serialization of List<IMyInterface> using ISerializable

谢谢参观!

我正在研究在现场部署的新产品版本。 我需要保持从旧软件反序列化现有文件的能力。

这是一个人为的例子:

我有一个现有的客户群,他们需要访问序列化文件。 出于这个问题的目的,他们有一个“Zoo”文件,里面有长颈鹿列表。

[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");
    }
}

现在,我们正在部署我们的“动物园”软件的新版本,我们将支持除长颈鹿以外的其他动物,我们应该首先这样做,但由于时间的限制,我们不得不用长颈鹿释放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");
    }
}

我正在使用自定义序列化绑定器将旧的长颈鹿反序列化为动物

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;
    }
}

问题是我不想让我的Zoo类使用List作为它的存储,而不是List。 我想这样做有两个原因,为了将来的可扩展性,这样我就可以更轻松地模拟单元测试。

反序列化新的IAnimal列表没有问题。 当我想反序列化旧样式项时,问题就出现了。 Binder返回正确的类型,调用正确的反序列化构造函数,一切看起来都正常,但List实际上已经满了null项。 一旦调用了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();
    }

任何建议将不胜感激。 我很难在这类事情上找到好的资源。 谢谢!

考虑从旧文件到新格式进行一次转换,最好是在安装时并且肯定在备份之后。 这样你就不必永远支持这种奇怪的一次性序列化,并且你的代码库变得非常简单。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM