浅析.NET中的Serialization

【Bundle包指针越界 Position out of bounds】 Mismatched serialization in the builtin class ‘AssetBundle’. The file ‘archive:/CAB-xxxxxx/CAB-xxxxxx’ is corrupted! Remove it and launch unity again! [Position out of bounds!] 阅读详情

摘要 本文简要介绍了.NET中的序列化(Serialization)概念,以及在代码中实作Serialization的方法。文章的最后介绍了Serialization在Clone方法中的运用。

 

Serialization的概念

 

Serialization是.NET中一种实现对象持久性(Persistent)的机制。它是一个将对象中的数据转换成一个单一元素(通常是Stream)的过程。它的逆过程是Deserialization。Serialization的核心概念是将一个对象的所有数据看作一个独立的单元。

 

一般说来,在两种情况下非常需要Serialization:1)当我们希望能够将对象当前的状态完整地保存到存储介质中,以便我们以后能够精确地还原对象时;2)当我们希望将对象从一个应用程序空间(Application domain)传递到另一个应用程序空间时。例如,Windows Form程序就是利用Serialization机制来实现剪贴板的copy & paste的。

 

.NET Framework支持两种类型的Serialization:Shallow Serialization和Deep Serialization。

 

所谓Shallow Serialization是将对象的可读写(read-write)属性的值转换成字节流,而对象内部的数据(没有通过read-write属性暴露出来的数据)则不被转换。XmlSerializer以及Web Services就使用这种技术。

 

Deep Serialization比Shallow Serialization更加彻底,因为它是将存储在对象私有变量里的实际值拷贝到字节流里。而且Deep Serialization还将serialize整个object graph。也就是说,如果你的对象持有其他对象的引用,或者其他对象引用的集合,那么所有这些对象都将被Serialize。BinaryFormatter和SoapFormatter以及.NET Remoting都使用Deep Serialization技术,它甚至被有限地用于LosFormatter来产生存储在Web Form页中的状态数据。

 

本文将着重于Deep Serialization。

 

Serialization的过程

 

.NET Framework通过Reflection提供自动Serialization的机制。当一个对象被序列化(Serialized)的时候,它的类名,Assembly,以及类实例的所有数据成员都将被写入存储介质中。Serialization引擎保持对所有已经被序列化的对象引用的追踪,以确保相同的对象引用最多只被序列化一次。

 

通常,一个Serialization过程会由formatter(例如BinaryFormatter)的Serialize方法引发。对象的Serialization过程按照以下规则进行:

1、   检测以确保formatter是否拥有一个代理选择器(surrogate selector)。如果有,检查代理选择器是否持有给定的对象类型。如果有,ISerializable.GetObjectData被调用。

2、   如果formatter没有代理选择器,或者代理选择器没有对象类型,检查对象是否被用Serializable属性标记。如果没有,则抛出SerializationException异常。

3、   如果对象被标记为Serializable,检查对象是否实现了ISerializable接口。如果实现了此接口,则GetObjectData被调用。

4、   如果对象没有实现ISerializable接口,则使用默认的序列化策略,来序列化没有用NonSerialized属性标记的域。

 

使你的class能够被序列化

 

通过上面对Serialization过程的分析,我们可以看出,有两种方式可以使一个class能够被序列化:1)将此class简单地标记为Serializable;2)为此class实现ISerializable接口,并将此class标记为Serializable。

 

1、   标记Serializable属性

 

标记Serializable属性的方式是实现Serialization的基本方法。举个简单的例子:

 

    [Serializable]

    public class Person

    {

        public string name = null;

        public int age = 0;

    }

 

你可以使用BinaryFormatter来将上面的class序列化:

 

        Person sam = new Person();

        sam.name = "sam";

        sam.age = 24;

        IFormatter formatter = new BinaryFormatter();

        Stream stream = new FileStream("sam.dat",

                    FileMode.Create, FileAccess.Write, FileShare.None);

        formatter.Serialize(stream, sam);

        stream.Close();

 

就是这么简单,你所要做的就是创建一个Stream和一个formatter的实例,然后调用formatter的Serialize方法。经过BinaryFormatter serialize的数据仍然能够通过BinaryFormatter deserialize回来,方法与serialize同样简单,这里就不赘述了。

 

如果你不想将类里的所有域都序列化,可以使用NonSerialized属性进行选择。如:

 

    [Serializable]

    public class Person

    {

        public string name = null;

        [NonSerialized]

        public int age = 0;

    }

 

这样,age域就不会被序列化了。

 

需要注意的是,Serializable属性并不能被继承。也就是说如果你希望Person的派生类也能够被Serialize的话,那么这个派生类也必须被Serializable标记。否则将得到SerializationException异常。

 

同样的,Person类中的所有对其他类的引用,其所引用的类都应该是能够被Serialize的。.NET Framework中的大部分class都实现了ISerializable接口,但有些class没有实现,例如ImageList。可以通过MSDN Library的到一个实现了ISerializable接口的class列表。对那些没有实现此接口的class,使用的时候要当心。

 

2、   实现ISerializable接口

 

Serializable属性的功能非常强大,它使得Serialize和Deserialize变得十分简单。但凡事有利必有弊,由Serializable实现的自动序列化方法有时不够灵活。我们并不能完全控制Serialize和Deserialize的行为,而有些时候它们的行为对我们来说很重要。那么我们通过何种方法能够控制Serialize和Deserialize的行为呢?答案就是,自己来实现ISerializable接口。ISerializable接口给予我们更大的自由来控制Serialize和Deserialize,但是无疑我们将不得不写更多的代码L。

 

下面我们来看看如何实现ISerializabe接口。ISerializable接口位于System.Runtime.Serialization名字空间中,声明如下:

 

       public inferface ISerializable

       {

              void GetObjectData(SerializationInfo info,

                            StreamingContext context);

       }

 

它只有一个方法GetObjectData。因此,像实现其他接口一样,我们必须实现此方法。但与其他接口不同的是,为了Deserialization,我们还必须实现一个特殊的构造函数(我称此构造函数为“序列化构造函数”),此构造函数具有与GetObjectData相同的参数列表。由于此构造函数专门用于.NET Framework在Deserialize时的Reflection机制,因此我们通常将它声明为保护或私有模式。如下:(当然,如果你的class只需要Serialize而不需要Deserialize的话,也可以不实现这个特殊的构造函数)

 

    [Serializable]

    public class Person : ISerializable

    {

        public string name = null;

        public int age = 0;

 

        public Person()

        {

        }

 

        protected Person(SerializationInfo info, StreamingContext context)

        {

            name = info.GetString("name");

            age = info.GetInt32("age");

        }

 

        void ISerializable.GetObjectData(SerializationInfo info,

                                        StreamingContext context)

        {

            info.AddValue("name", name);

            info.AddValue("age", age);

        }

    }

 

通过实现ISerializable接口,使得我们有机会在ISerializable.GetObjectData中控制Serialize的行为,在“序列化构造函数”中控制Deserialize的行为。这个接口提供给我们的信息非常全面而灵活,以致于我们甚至可以在这两个方法中耍些花招。比如,我们可以在Deserialize的时候,籍由改变info.FullTypeName来得到一种与被Serialize的对象不同类型的另一个对象等。

 

独辟蹊径

 

前面谈到过Serialization被运用的典型环境,是对象存储、进程间数据传递等涉及到对象持久性的领域。但实际上,它也能够被运用到其他的许多地方,关键在于我们是否能想到去用运Serialization,有时候思维定式也是很可怕的J。举个例子,我们来看看在Clone方法中如何使用Serialization[1]。

 

如果我们要为Person类实现Clone方法,我们通常会这样写:

 

    [Serializable]

    public class Person : ICloneable

    {

        public string name = null;

        public int age = 0;

 

        public object Clone()

        {

            Person person = new Person();

            person.name = name;

            person.age = age;

 

            return person;

        }

    }

 

如果我们利用Serialization的方法,Clone函数就能写成下面的样子:

 

        public object Clone()

        {

            MemoryStream stream = new MemoryStream();

            BinaryFormatter formatter = new BinaryFormatter();

           

            formatter.Serialize(stream, this);

            stream.Position = 0;

            return formatter.Deserialize(stream);

        }

 

从这两个实现上看,使用Serialization实现Clone方法似乎并没有什么好处。可是设想如果你面对的是一个复杂的类继承体系,从基类到派生类都需要实现Clone方法。利用第一种实作手法,你将不得不为每一个class写一个Clone方法,而且随着数据成员的增多,这个方法将越来越冗长,并且会由于数据成员的改变而引发错误(我曾经遇到过好几次,由于class中增加了成员变量,而Clone方法没有及时更新,导致运行时错误。呵呵,这种错误还很难调试)。现在你看到用Serialization实现的好处了吧?是的,我们只要在基类中将Clone方法声明为virtual,并用Serialization的方法实现之,然后保证基类和派生类都可以被Serialize,上面所有的麻烦不都迎刃而解了吗?

 

总结

 

现代软件项目中,无论何种项目都会或多或少地涉及到对象持久性的问题,.NET也不例外,无论是Windows Form、ASP.NET,还是Web Services,都需要处理对象持久性。而Serialization正是.NET为应对这个问题而给出的解法。

 

参考文献

 

·[1] Rockford Lhotka,《Object Serialization in Visual Basic .NET》,MSDN Library。Serialization在Clone方法中的运用即来自此文。

 

·Piet Obermeyer and Jonathan Hawkins,《Object Serialization in the .NET Framework》,MSDN Library。

 

·Jeffrey Richter,《.NET Run-time Serialization》Part 1,Part 2,Part 3,MSDN Library。

Python + DeepSeek 完美结合:本地部署提效指南 随着生成式 AI 和深度学习技术的快速发展,如何将智能搜索工具与本地部署结合以提升效率成为了许多企业和开发者关注的重点。DeepSeek 是一款开源的智能搜索框架,结合 Python 的灵活性,能够实现高效、可靠的本地部署方案。本文将介绍如何通过 Python 和 DeepSeek 的完美结合,实现本地化部署并提升工作效率。通过将 Python 与 DeepSeek 结合,可以快速实现语义搜索的本地化部署,确保数据安全的同时提升工作效率。文件可直接作为 DeepSeek 的输入数据。 阅读详情

相关推荐

陀螺仪及四元数解算

陀螺仪 四元数

zb313982521的博客 2004

Bake工作流下,如何实现卡通化渲染?

这是第144篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。 UWA 问答社区:answer.uwa4d.com UWA QQ群2:793972859(原群已满员) 本期目录: Bake工作流下,如何实现卡通化渲染 Unity版本升级问题 使用AssetBundle报错 C++ dll的.net版本无法更改 je...

UWA—简单优化,优化简单! 1018

DSP28335学习笔记——McBSP配置为SPI

由于28335硬件外设上只有一个SPI接口,所以当SPI接口不够用时,可以将McBSP配置为SPI接口。 参考28335的数据手册,当DSP为主机时,配置McBSP为SPI所用到的接线如下图所示。 另外总结一些调试过程中的经验。 1.传输数据位数的确定 由RCR1寄存器中的RWDLEN1和XCR1寄存器中的XWDLEN1决定。分别对应着接受和发送的位数,在配置成SPI模式的时候,这

捕虫少年 1万+

Unity游戏运行闪退问题定位与解决(标签:Oops、crashed、System out of memory、UnityEngine.AssetBundle:LoadAsset_Internal)

一、闪退问题 最近Unity工程发布PC平台的包,运行游戏,出现了一个闪退的问题,弹框如下。 根据提示,找到对应的crash日志。 打开output_log.txt,如下: ... ... The file 'archive:/CAB-350107fab3529178780193de85391267/CAB-350107fab3529178780193de85391267' is corrupted! Remove it and launch unity again! [Position out of

linxinfa的专栏 1万+

不用变压器也能联网?手把手教你PHY芯片直连的3种实用方案

本文详细介绍了PHY芯片直连的3种实用方案,帮助开发者在物联网设备中实现更经济、更精简的网络连接设计。通过分析电压驱动型和电流驱动型PHY的核心原理,提供具体的电路配置和优化建议,确保信号质量的同时节省成本和空间。

smartcontract5的博客 332

从jieba分词到BERT-wwm——中文自然语言处理(NLP)基础分享系列(12)

今天是本系列的最后一期,完整代码呈现Huggingface transformers平台上微调BERT-wwm-ext的过程方法。

weixin_49518391的博客 3763

U3D 部分问题

.编译工程时生成了 Mismatched serialization in the builtin class 'Mesh'. (Read 560 bytes but expected 561 bytes) 低版本编辑器打开高版本编辑器序列化过的对象就会报类似错,建议更换高版本编辑器

Kaitiren的专栏 3828

2024年03月CCF-GESP编程能力等级认证Python编程五级真题解析

下面流程图在yr输入2024时,可以判定yr代表闰年,并输出 2月是29天 ,则图中菱形框中应该填入( )。 A. (yr % 400 == 0) or (yr % 4 == 0) B. ( yr % 400 == 0) or (yr % 4 == 0 and yr %100 != 0) C. ( yr % 400 == 0) and ( yr % 4 == 0) D. (yr % 400 == 0) and (yr % 4 == 0 and yr % 100 != 0)

人生就是一个不断学习的过程 745

vue-markdown-loader 渲染出来的不一样_【厚积薄发】Bake工作流下,如何实现卡通化渲染?...

这是第144篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。UWA 问答社区:answer.uwa4d.comUWA QQ群2:793972859(原群已满员)本期目录Bake工作流下,如何实现卡通化渲染Unity版本升级问题使用AssetBundle报错C++ dll的.net版本无法更改jenkins执行-exe...

weixin_39701288的博客 278

毕设助力!从0到1构建基于YOLO的家居安防异常活动监测系统,让你的毕设守护家庭安全

做完这个项目,你会发现自己完整走过了“数据采集→标注→模型训练→功能集成→界面开发”的毕设全流程,这不仅是技术能力的证明,更是对“技术守护生活”这一理念的实践。答辩时,你可以重点讲这几点:系统解决了家居安防的哪些痛点、对比了不同YOLO模型的性能差异、系统未来能怎么优化(比如对接智能门锁/报警器、实现语音交互)。未来,这个系统还能往“更智能”的方向发展:比如结合行为分析判断异常意图、开发移动端APP实现全屋设备联动、在智慧社区中实现多家庭联防……想象空间很大。

LXY_LOVE36的博客 120

Open3D点云算法与点云深度学习案例汇总(长期更新)

Open3D-python算法专栏文章汇总,长期更新算法原理与实战案例。

纵马踏花向自由 7467

[转] 浅析.NET中的Serialization

Serialization的概念 Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1403819Serialization是.NET中一种实现对象持久性(Persistent)的机制。它是一个将对象中的数据转换成一个单一元素(通常是Stream)的过程。它的逆过程是Deserialization。Serialization...

dengtaojiu7237的博客 126

AUTOSAR TcpIp模块 以太网配置开发2

someip协议之tcpIp协议

汽车电子电气 1085
上一篇: 如何在.NET中调试设计时(design-time)控件(下)
下一篇: 谈谈.NET中的内存管理
sam1111
博客等级 码龄26年 18粉丝 28原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值