建议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个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程的更多相关文章

  1. 【转】编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

    建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...

  2. 编写高质量代码改善C#程序的157个建议

    1.使用StringBuilder或者使用string.Format("{0}{1}{2}{3}", a, b, c, d)拼接字符串. 2.使用默认转型方法,比如使用类型内置的P ...

  3. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  4. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  5. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  6. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  7. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  8. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  9. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

随机推荐

  1. 【openCV学习笔记】【2】读取并播放一段视频

    #include <iostream> #include <opencv/highgui.h> int main(int argc, char** argv){ cvNamed ...

  2. 搭建JIRA汉化后乱码问题

    JIRA的简体中文乱码(使用了BIG5导致) 安装完JIRA汉化后发现一部分中文显示乱码,经检查:本来应该是UTF-8编码的却显示成Big5,Big5根本不支持简体中文的! 进入JIRA数据库后找到 ...

  3. SpringBean生命周期

    Spring作为当前Java最流行.最强大的轻量级框架,受到了程序员的热烈欢迎.准确的了解Spring Bean的生命周期是非常必要的.我们通常使用ApplicationContext作为Spring ...

  4. [PYTHON 实作] 算100

    问题:编写一个在1,2,…,9(顺序不能变)数字之间插入+或-或什么都不插入,使得计算结果总是100的程序,并输出所有的可能性.例如:1 + 2 + 34 – 5 + 67 – 8 + 9 = 100 ...

  5. linux composer的使用

    安装好的composer使用很简单,分两步加载组件例1.搜索组件:composer search phpexcel2.下载组件:composer require 厂商名/包名然后在使用该插件的脚本中引 ...

  6. SE新手游操控创新:一个按键=五个技能

    转自:http://www.gamelook.com.cn/2015/01/201299 GameLook报道 / 日本游戏厂商一向擅长搞发明创造,除了诞生了各种烧脑奇葩游戏以外,日本主流手机游戏的核 ...

  7. 海外开发者推荐:10个顶级2D游戏资源站

    转自:http://www.gamelook.com.cn/2015/12/239038 Gamelook报道/随着手游市场的持续增长,HTML5的发展以及大型发行商的支持,2D游戏的数量变得越来越多 ...

  8. 温故而知新java事务

    一.什么是Java事务 通常的观念认为,事务仅与数据库相关. 事务必须服从ISO/IEC所制定的ACID原则.ACID是原子性(atomicity).一致性(consistency).隔离性 (iso ...

  9. VMware80端口映射

    目标是外网访问80端口,然后映射到虚拟机的80端口,80映射到80. 1.首先80端口是最常用的端口,要确认主机80端口是否被占用,如果被占用,停止或者修改占用80端口程序. 2.80端口默认防火墙是 ...

  10. kali下安装go环境

    1.安装go 下载安装包,命令:wget -c https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz 下载完后,解压到 /u ...