ISerializable是否与具有较少字段的以前版本的类向后兼容?

时间:2015-09-17 22:11:51

标签: c# serialization deserialization backwards-compatibility

对不起,如果我说这个问题有点奇怪!基本上,我有一个可序列化的类,在当前时间只有一个字段,但随着我们向系统添加功能,将来肯定会获得更多。序列化过程将用于将实例传递给WCF服务,以及从/向文件读取/写入它。当然,如果我不断用额外的字段更新课程,后者可能是个大问题。

幸运的是,我认为我已经通过在构造函数中的字段设置器周围添加了一个try / catch块来解决这个问题,我也会对添加到类中的任何其他字段执行此操作。

/// <summary>
/// Represents launch options supplied to an executable.
/// </summary>
[Serializable]
public sealed class ExecutableLaunchOptions : ISerializable
{
    /// <summary>
    /// Creates a new set of executable launch options.
    /// </summary>
    public ExecutableLaunchOptions() { }

    /// <summary>
    /// Creates a new set of executable launch options via deserialization.
    /// </summary>
    /// <param name="info">the serialization information.</param>
    /// <param name="context">the streaming context.</param>
    public ExecutableLaunchOptions(
        SerializationInfo info, StreamingContext context) : this()
    {
        // Get the value of the window style from the serialization information.
        try { this.windowStyle = (ProcessWindowStyle)info.GetValue(nameof(this.windowStyle), typeof(ProcessWindowStyle)); } catch { }
    }

    // Instance variables.
    private ProcessWindowStyle windowStyle = ProcessWindowStyle.Normal;

    /// <summary>
    /// Gets or sets the window style to apply to the executable when it launches.
    /// </summary>
    public ProcessWindowStyle WindowStyle
    {
        get { return this.windowStyle; }

        set { this.windowStyle = value; }
    }

    /// <summary>
    /// Gets the information required for the serialization of this set of launch options.
    /// </summary>
    /// <param name="info">the serialization information.</param>
    /// <param name="context">the streaming context.</param>
    public void GetObjectData(
        SerializationInfo info, StreamingContext context)
    {
        // Add the value of the window style to the serialization information.
        info.AddValue(nameof(this.windowStyle), this.windowStyle, typeof(ProcessWindowStyle));
    }
}

我猜这将允许我在反序列化时保留与包含该类的先前版本的实例的文件的向后兼容性,因为代码将简单地抛出并随后捕获不存在的每个字段的异常。序列化信息,将其值保留为默认值。我在这里是正确的,还是我错过了什么?

2 个答案:

答案 0 :(得分:0)

使用FormatterAssemblyStyle.Simple格式化程序对对象进行序列化将允许您读取序列化对象的旧版本。使用[OptionalField]标记新字段将允许旧版本的应用程序打开新的序列化文件而不会抛出。所以你应该使用它们吗?不,不,不,不,不,没有。

序列化是为在进程之间交换数据而设计的,它不是,也不是数据持久性机制。格式在过去已经发生了变化,并且将来可能会发生变化,因此将它用于您希望将来使用新的.NET版本再次打开的持久数据是不安全的。

BinaryFormatter算法是专有的,因此使用这些数据编写非.NET应用程序非常困难。

对于包含多个属性的数据,序列化效率很低,因为它需要反序列化整个对象才能访问任何字段。如果数据包含像图像这样的大数据,这尤其成问题。

如果您的数据不需要随机访问,我建议将其序列化为JSON或XML等文本格式。如果数据很大,则应考虑压缩文本编码数据。

如果您需要随机访问数据,则应调查MySQL或SQL Server CE等数据存储。

答案 1 :(得分:0)

您可以使用SerializationInfo.GetEnumerator()循环浏览SerializationInfo中包含的名称 - 值对,以查找可能仅有条件存在的项目:

    public ExecutableLaunchOptions(
        SerializationInfo info, StreamingContext context) : this()
    {
        // Get the value of the window style from the serialization information.
        var enumerator = info.GetEnumerator();
        while (enumerator.MoveNext())
        {
            var current = enumerator.Current;
            if (current.Name == "windowStyle" && current.ObjectType == typeof(ProcessWindowStyle))
            {
                this.windowStyle = (ProcessWindowStyle)current.Value;
            }
        }
    }

请注意,这个类太旧了(即从c#1.0开始),尽管有GetEnumerator()方法,它实际上并没有实现IEnumerable

如果您经常需要这样做,可以引入类似的扩展方法:

public static class SerializationInfoExtensions
{
    public static IEnumerable<SerializationEntry> AsEnumerable(this SerializationInfo info)
    {
        if (info == null)
            throw new NullReferenceException();
        var enumerator = info.GetEnumerator();
        while (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
    }
}

然后做:

    public ExecutableLaunchOptions(
        SerializationInfo info, StreamingContext context) : this()
    {
        foreach (var current in info.AsEnumerable())
        {
            if (current.Name == "windowStyle" && current.ObjectType == typeof(ProcessWindowStyle))
            {
                this.windowStyle = (ProcessWindowStyle)current.Value;
            }
        }
    }

这更具现代性和可读性。