建议56:使用继承ISerializable接口更灵活地控制序列化过程

来源:互联网 发布:保留两位小数 java 编辑:程序博客网 时间:2024/05/01 02:46

建议56:使用继承ISerializable接口更灵活地控制序列化过程

接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttribute、OnDeserializingAttribute、OnSerializedAttribute、OnSerializingAttribute、NoSerializable等特性不能完全满足自定义序列化的要求,那就需要继承ISerializable了。

 

以下是格式化器的工作流程:如果格式化器在序列化一个对象的时候,发现对象继承了ISerializable接口,那它就会忽略掉类型所有的序列化特性,转而调用类型的GetObjectData方法来构造一个SerializationInfo对象,方法内部负责向这个对象添加所有需要序列化的字段(“添加”这个词可能不太恰当,因为我们在添加前可以随意处置这个字段)。以建议55中的例子为例,如果要为ChineseName构造对应的值,在类继承ISerializable接口的情况下,应该这样去实现:

复制代码
    class Program    {        static void Main()        {            Person liming = new Person() { FirstName = "Ming", LastName = "Li" };            BinarySerializer.SerializeToFile(liming, @"c:\", "person.txt");            Person p = BinarySerializer.DeserializeFromFile<Person>(@"c:\person.txt");            Console.WriteLine(p.FirstName);            Console.WriteLine(p.LastName);            Console.WriteLine(p.ChineseName);                   }    }    [Serializable]    public class Person : ISerializable    {        public string FirstName;        public string LastName;        public string ChineseName;        public Person()        {        }        protected Person(SerializationInfo info, StreamingContext context)        {            FirstName = info.GetString("FirstName");            LastName = info.GetString("LastName");            ChineseName = string.Format("{0} {1}", LastName, FirstName);        }        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)        {            info.AddValue("FirstName", FirstName);            info.AddValue("LastName", LastName);        }    }
复制代码

序列化工具类:

复制代码
    public class BinarySerializer    {        //将类型序列化为字符串        public static string Serialize<T>(T t)        {            using (MemoryStream stream = new MemoryStream())            {                BinaryFormatter formatter = new BinaryFormatter();                formatter.Serialize(stream, t);                return System.Text.Encoding.UTF8.GetString(stream.ToArray());            }        }        //将类型序列化为文件        public static void SerializeToFile<T>(T t, string path, string fullName)        {            if (!Directory.Exists(path))            {                Directory.CreateDirectory(path);            }            string fullPath = Path.Combine(path, fullName);            using (FileStream stream = new FileStream(fullPath, FileMode.OpenOrCreate))            {                BinaryFormatter formatter = new BinaryFormatter();                formatter.Serialize(stream, t);                stream.Flush();            }        }        //将字符串反序列化为类型        public static TResult Deserialize<TResult>(string s) where TResult : class        {            byte[] bs = System.Text.Encoding.UTF8.GetBytes(s);            using (MemoryStream stream = new MemoryStream(bs))            {                BinaryFormatter formatter = new BinaryFormatter();                return formatter.Deserialize(stream) as TResult;            }        }        //将文件反序列化为类型        public static TResult DeserializeFromFile<TResult>(string path) where TResult : class        {            using (FileStream stream = new FileStream(path, FileMode.Open))            {                BinaryFormatter formatter = new BinaryFormatter();                return formatter.Deserialize(stream) as TResult;            }        }    }
复制代码

我们在方法GetObjectData中处理序列化,然后在一个带参数的构造方法中处理反序列化。虽然在接口中没有地方指出需要这样一个构造器,但这确实是需要的,除非我们序列化后不再打算把它反序列化回来。

这个例子不能表现出ISerializable接口比Serializable特性的优势,见下面的例子:

将Person序列化,然后在反序列化中将其变为另一个对象:PersonAnother类型对象。要实现这个功能,需要Person和PersonAnother都实现ISerializable接口,原来其实很简单,就是在Person类的GetObjectData方法中处理序列化,在PersonAnother的受保护构造方法中反序列化。

复制代码
    class Program    {        static void Main()        {            Person liming = new Person() { FirstName = "Ming", LastName = "Li" };            BinarySerializer.SerializeToFile(liming, @"c:\", "person.txt");            PersonAnother p = BinarySerializer.DeserializeFromFile<PersonAnother>(@"c:\person.txt");            Console.WriteLine(p.Name);        }    }    [Serializable]    class PersonAnother : ISerializable    {        public string Name { get; set; }        protected PersonAnother(SerializationInfo info, StreamingContext context)        {            Name = info.GetString("Name");        }        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)        {        }    }    [Serializable]    public class Person : ISerializable    {        public string FirstName;        public string LastName;        public string ChineseName;        public Person()        {        }        protected Person(SerializationInfo info, StreamingContext context)        {        }        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)        {            info.SetType(typeof(PersonAnother));            info.AddValue("Name", string.Format("{0} {1}", LastName, FirstName));        }    }
复制代码

在Person类型的GetObjectData方法中,有句代码非常重要:

info.SetType(typeof(PersonAnother));

它负责告诉序列化器:我要被反序列化为PersonAnother。而类型PersonAnother则很简单,它甚至都不需要知道谁会被反序列化成它,它不需要做任何特殊处理。

ISerializable接口这个特性很重要,如果运用得当,在版本升级中,它能处理类型因为字段变化而带来的问题。

 

 

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

0 0
原创粉丝点击