热门标签 | HotTags
当前位置:  开发笔记 > 程序员 > 正文

NET常见类系列探究——序列化和反序列化的应用

l为什么要序列化?为了达到软件“人性化”的目的,很多开发制作软件的程序商们非常习惯将某些预订好的设置(诸如“皮肤”等)设定成最佳状态,保存到一个信息文件中;或者当用户改变这一状态时

 l       为什么要序列化?

为了达到软件“人性化”的目的,很多开发制作软件的程序商们非常习惯将某些预订好的设置(诸如“皮肤”等)设定成最佳状态,保存到一个信息文件中;或者当用户改变这一状态时软件自身也会将用户设置的状态记载下来,这样用户下次开启软件就不必费神费力地重新去设置适合自己的软件布局状态了。

       像这种“设置记载”的做法最早(当然,现在仍然保留着)是使用类似于文本文件的形式(ini文件)进行存储,大概格式如下:

 

[区域块名称]

;注释

变量(字段)名=值

 

当一个软件开启的时候,它首先加载该文件(如果该文件存在,没有人为改动出现异常的情况下),其步骤分别是:1)读取相关设置的区域块名称(标识符,表明统一的某个模块的设置)2)读取其名下所有的变量(字段,又称属性)和值 3)根据标识符和变量要求,改变软件界面,呈现给用户看。

 

比如说一个股票软件的主界面(呈现用户自选股)的页面,就应该包含诸如“背景色”、“涨的颜色”、“跌的颜色”等属性。那么按照中国大陆的习惯,可能设置文件中应该存在这样的信息:

 

[SelfChoose]

;选股票的SQL

Sql=”select * from Customers where cid=?”

;背景色

BackGroundColor=black

;涨的颜色

RisingUp=red

;跌的颜色

DropDown=green

 

这样做当然是一目了然的,但是由于是文本文件,所以客户可以轻易打开直接进行编辑修改。如果有些小孩或者是恶意的黑客们加入了些具备破坏性的代码,其结果可想而知。而且读取这样的文件虽然可以借助微软的DLL类库(WritePrivateProfileStringGetPrivateProfileString),但是读取的时候仍旧是离散的,每次读入一个值,还要根据“区域块名”和某个隶属的“变量名”决定究竟是设置改变哪个块的哪个部分,如果一个软件有很多设置的话代码量是非常可怕的。

 

后来经过改进,微软推出了注册表和XML来替代它,这两种方式都有自己的对应类和接口等操作函数(方法),不过XML的安全性还是不高(仍旧基于文本格式,可以打开直接修改),至于注册表一旦写坏,用户就有“冒着重装系统的危险”。

现在的问题在于:如何让客户的设置按照预期的目标进行存储,并且要符合OOP的思想,而且又要做到安全性高(最好是用记事本无法打开,或者打开根本看不懂)?

 

要做到这点,我们不得不考虑这样的设计:

1)基于OOP思想的设置:它本身必须是一个类,因为只有这样才可能符合OOP的逻辑(不一定有方法和事件,但私有变量和公开属性必不可少);最好做到这个文件读入不是分批、离散字符串式,而是通过某种途径可以还原成一个类进行操作。

2)安全性:要么加密,要么以二进制方式直接存储对象。

 

幸运的是,微软已经帮助我们实现了这种模式;这就是下面要讨论的“OOP序列化”问题。

 

l       如何序列化?

将某个软件模块以及其属性抽象出来,写成一个普通类(一般无方法事件,存在公开属性和私有变量),比如上面“股票设置”例子可以写成这样一个类:

Code
[Serializable]
public class SelfChoose
{
    
private const string sql=”select * from Customers where cid={0}”;
    
public string SQL
    {
        
return string.Format(sql,Id);
    }

    
private int id=0;
    
public int Id
    {
        
//省略Get、Setf属性
    }

    
//省略BackGround、RisingUp和DropDown的属性
}

当需要读取的时候,在界面的Load的事件中可以这样写:

 

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

private Form_Load(sender as object, e as EventArgs)
{
    
using (FileStream fs=new FileStream(序列化文件存放位置,FileMode.Open))
    {
        BinaryStream bs
=new BinaryStream();
        SelfChoose sc
=bs.Deserialize(bs) as SelfChoose;
    窗体的名称.BackGround
=sc. BackGround
……
    }
}

当用户关闭窗体的时候,在关闭的事件中保存用户的设置:

Code
private Form_Closing(sender as object, e as EventArgs)
{
    
using (FileStream fs=new FileStream(序列化文件存放位置,FileMode.OpenOrCreate))
    {
SelfChoose sc
=new SelfChoose();
sc.BackGroundColor
=窗体颜色. BackGroundColor
//其余属性设置……
BinaryStream bs=new BinaryStream();
bs.Serialize(fs,sc);
    }
}
 

这里需要注意几个问题:

1)要(反)序列化的时候,必须引入“using System.Runtime.Serialization.Formatters.Binary;”

命名空间。

2BinaryStream是一个二进制文件的读写类,其中包含着两个方法:

       Ivoid Serialize (Stream s, object obj):要被写入(序列化)的类以及写入的流(写入的位置)。

       IIobject Deserialize (Stream s):要被读出类(反序列化)的源(注意还原成原来的类的时候需要强制类型转换)。

 

l       (反)序列化的另类妙用

大家不知道是否注意这样一个问题——为什么BinaryStream的输入、输出参数竟然是Stream(而不是FileStream)?难道说(反)序列化不仅仅是用来做设置记录一类的?

我们说Stream是文件读写流的一个抽象类,许多IO类(FileStreamMemoryStream等)都继承了该类。现在就来使用MomoryStream类构造一个简单的“深复制”方法

所谓“深复制”,就是指将类自身中的所有数据——无论什么类型,完全克隆拷贝一份返回给用户(某些类实现了“Clone”方法,但是都是“潜复制”,即把自身直接返回出去——return this)这样做的问题显然会带来一个问题——那就是“牵一发动全身”(任何地方改变了这个副本类的某个属性,则原来版本也“同步”变更了)。而且,如果克隆的类中带有引用类型(诸如自定义类或数组),则问题更加复杂化。传统的做法往往是一一拷贝这些引用类型的副本,但是这样做显然费时费力。

现在既然(反)序列化可以把类存储在文件中,并且还原成原始保存的状态,那么为何不使用这种手段进行“深复制”呢?不过,要提醒诸位的是,(反)序列化实现深复制不必每次把类存储在特定文件中,只要在内存中进行就可以了,所以要使用到MemoryStream。典型代码如下:

 

Code
public class XXXX:ICloneable
{
    
//引用类型、值类型和自定义类型……,Get、Set方法
    public override object Clone()
    {
        MemoryStream ms
=new MemoryStream();
        BinaryStream bs
= BinaryStream();
        bs.Serialize(ms,
this);
        bs.Position
=0;
        
return bs.Deserialize(ms):
    }
}

以上XXX类实现了ICloneable接口方法,使用了内存式的“(反)序列化克隆”手段达到了目的。此处注意两点:

1)不能使用using代码块,因为using代码块会隐含调用Dispose()方法,而MemoryStream没有这个方法(没有实现IDisposable接口)。

2)序列化之后,必须设置Position=0,否则发生异常(因为MemoryStream在序列化之后指针已经在末尾了,此时反序列化需要从头读取的;FileStream不存在这样的问题)。


推荐阅读
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
author-avatar
_妃妃教主
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有