1

我有一个类,应该支持版本容错序列化

[Serializable]
class A {
    [OptionalField]
    int a;

    [OptionalField]
    MyClass b;

    [OptionalField]
    MyClass c;
}
  • 反序列化后如何更正缺失的字段?我想,我必须使用标有[OnDeserializing]. 但是我怎样才能得到哪些字段被忽略了?
  • 我可以配置自动反序列化以通过默认构造函数初始化字段以防它们丢失吗?
4

2 回答 2

3

此外,您可以使用 OnSerializingAttribute 和 OnSerializedAttribute 来设置字段。如示例所示,已设置的字段将保留其值。但是请注意,只有在 OnSerializing 事件期间设置了该字段时才会出现这种情况。OnSerialized 事件期间设置的字段将覆盖序列化值。

编辑:在这种情况下,如果字段等于 null 并在必要时实例化,您可以检查您的方法(用 OnSerialized 装饰)。如果有可能永远不会使用此字段并且可以推迟创建它,请考虑将有问题的字段隐藏在属性后面并延迟实例化它。

模型.cs:

using System;
using System.Runtime.Serialization;

namespace SerializationExample
{
    [Serializable]
    public class Model
    {
        public Model(){
            A = new SomeClass();
        }

        [OptionalField]
        public int value;

        [OptionalField]
        public SomeClass A;

        [OptionalField]
        public AnotherClass B;            

        [OnDeserializing]
        void OnDeserializing(StreamingContext context)
        {
            B = new AnotherClass("Set during deserializing");
        }

        [OnDeserialized]
        void OnDeserialized(StreamingContext context)
        {
            // Do sth. here after the object has been deserialized
        }

        public override string ToString()
        {
            return String.Format("A: {0}\nB: {1}", A, B);
        }

    }

    [Serializable]
    public class SomeClass
    {
        public string Value { get; set; }

        public SomeClass()
        {
            Value = "Default";
        }

        public override string ToString()
        {
            return Value;
        }
    }

    [Serializable]
    public class AnotherClass
    {
        public string Value { get; private set; }

        public AnotherClass(string v)
        {
            Value = v;
        }

        public override string ToString()
        {
            return Value;
        }
    }
}

程序.cs:

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializationExample
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] FileNames = new string[] {
                @"model1.bin",
                @"model2.bin"
            };

            Stream[] files = new Stream[] {
                File.Create(FileNames[0]),
                File.Create(FileNames[1])
            };

            BinaryFormatter bf = new BinaryFormatter();

            Model m1 = new Model();
            m1.B = new AnotherClass("Set in app");
            m1.A.Value = "Set in app";

            Model m2 = new Model();

            Console.WriteLine("M1:\n{0}\n", m1);
            Console.WriteLine("M2:\n{0}\n\n", m2);

            bf.Serialize(files[0], m1);
            bf.Serialize(files[1], m2);

            foreach (var f in files)
                f.Seek(0, SeekOrigin.Begin);

            m1 = null;
            m2 = null;

            m1 = (Model)bf.Deserialize(files[0]);
            m2 = (Model)bf.Deserialize(files[1]);

            Console.WriteLine("M1:\n{0}\n", m1);
            Console.WriteLine("M2:\n{0}\n\n", m2);

            foreach (var f in files)
                f.Close();           
        }
    }
}

输出:

M1:
A: Set in app
B: Set in app

M2:
A: Default
B:


M1:
A: Set in app
B: Set in app

M2:
A: Default
B: Set during deserializing
于 2012-08-31T21:26:30.390 回答
0

如果您只想将这些值初始化为默认值,那么您只需要一个默认的无参数构造函数来初始化它们。这将在反序列化期间被调用,并且任何丢失的字段都将保留您在构造函数中初始化它们的任何值。

如果你想要更多的控制,你可以ISerializable在你的类上实现接口和适当的构造函数(你通常应该同时做这两个,尽管通常不需要一个或另一个。)

如果 C# 找到带有签名的构造函数:

protected A ( SerializationInfo info, StreamingContext context )
{
}

它将调用该构造函数,并传递一个包含所有序列化信息的弱类型字典。(您可以使用ISerializable::GetObjectData将自定义字段写入对象并在构造函数中检索它们)。您可以使用这些info.GetXXX方法来提取这些值。

需要注意的一点:如果你实现了这个构造函数,你必须完成所有的工作,包括通常会自动反序列化的字段。对于任何缺少的字段,只需适当地设置它们。同样,如果您实现GetObjectData,则必须序列化该方法中的所有内容。这很简单,但是如果你改变你的类,你需要适当地编辑你的自定义方法/构造函数。

于 2012-08-31T20:55:53.513 回答