体系化和反类别化(2)---[Serializable] 转

c#中的对象概略分为值类型和援用类型,值类型大约满含 int, string, struct等,引用类型大约包蕴 自定义Class,object 等。

摘要:

C# Serializable

System.SerializableAttribute

串行化是指积累和获得磁盘文件、内部存款和储蓄器或任哪儿方中的对象。在串行化时,全数的实例数据都封存到存款和储蓄媒介物上,在撤废串行化时,对象会被还原,且不能够与其原实例差距开来。

只需给类加多Serializable属性,就足以达成串行化实例的成员。

并行化是串行化的逆进度,数据从存款和储蓄媒质中读抽取来,并赋给类的实例变量。

例:

图片 1

 1 [Serializable]
 2 public class Person
 3 {
 4     public Person()
 5     {
 6     }
 7 
 8     public int Age;
 9     public int WeightInPounds;
10 }

图片 2

 

上边来看二个小例子,首先要加多命名空间

using System.Runtime.Serialization.Formatters.Binary;

 

下边包车型客车代码将指标Person进行体系化并积攒到多个文本中

图片 3

 1 Person me = new Person();
 2 
 3 me.Age = 34;
 4 me.WeightInPounds = 200;
 5 
 6 Stream s = File.Open("Me.dat",FileMode.Create);
 7 
 8 BinaryFormatter bf = new BinaryFormatter();
 9 
10 bf.Serialize(s,me);
11 
12 s.Close();

图片 4

 

下一场再举二个并行化的例证

图片 5

 1 Stream s = File.Open("Me.dat",FileMode.Open);
 2 
 3 BinaryFormatter bf = new BinaryFormatter();
 4 
 5 object o = bf.Deserialize(s);
 6 
 7 Person p = o as Person;
 8 if(p != null)
 9     Console.WriteLine("DeSerialized Person aged:{0} whight:{1}",p.Age,p.WeightInPounds);
10 
11 s.Close();

图片 6

 

假若须求对有的字段种类化部分不类别化时,大家能够服从如下设置达成

图片 7

 1 [Serializable]
 2 public class Person
 3 {
 4     public Person()
 5     {
 6     }
 7 
 8     public int Age;
 9     [NonSerialized]
10     public int WeightInPounds;
11 }

图片 8

 

Serializable在C#中的功能.NET 中的对象连串化
简介
  种类化是指将对象实例的场合存款和储蓄到存款和储蓄媒体的长河。在这里进度中,先将目的的公家字段和村办字段以至类的称号(包蕴类所在的程序集)转变为字节流,然后再把字节流写入数据流。在跟着对目的进行反体系化时,将创造出与原对象完全相近的副本。
图片 9
   在面向对象的情形中贯彻体系化学工业机械制时,必得在易用性和灵活性之间实行局地衡量。只要你对此进程有丰盛的调节手艺,就足以使该进程在一点都不小程度上机关实行。 比方,不难的二进制类别化不能够满意急需,也许,由于特定原因供给鲜明类中那一个字段供给系列化。以下各部分将追查 .NET 框架提供的可信赖的连串化学工业机械制, 并注重介绍使您能够依据要求自定义体系化进度的部分关键意义。

有始有终存款和储蓄
   大家经常索要将目的的字段值保存到磁盘中,并在之后检索此数额。即便不利用体系化也能一气浑成那项专门的学问,但这种艺术常常很麻烦并且便于出错,并且在供给追踪对象的档期的顺序布局时,会变得越来越复杂。能够想象一下编纂包括多量对象的特大型业务应用程序的气象,技士必须要为每三个指标编排代码,以便将字段和质量保存 至磁盘甚至从磁盘还原这个字段和性质。类别化提供了轻巧达成那些目标的快捷方法。
图片 10
   公共语言运维时 (CLEscort卡塔尔 管理对象在内部存储器中的分布,.NET 框架则透过利用反射提供自动的类别化学工业机械制。对象系列化后,类的称谓、程序集以至类实例 的具有数据成员均被写入存储媒体中。对象经常用成员变量来积存对任何实例的援用。类类别化后,体系化引擎将追踪全数已系列化的援引对象,以确认保障同等对象不 被系列化多次。.NET 框架所提供的系列化连串布局得以活动准确管理对象图表和巡回援引。对指标图表的唯大器晚成必要是,由正在扩充类别化的靶子所援用的全体对象都一定要标识为 Serializable(请参阅基本体系化)。不然,当类别化程序试图系列化未标识的对象时将会现身非凡。
图片 11
  当反系列化已系列化的类时,将再次创设该类,并机关还原全部数据成员的值。

按值封送
   对象仅在创造对象的施用程序域中有效。除非对象是从 MarshalByRefObject 派生拿到或标记为 Serializable,否则,任何 将对象作为参数字传送递或将其看作结果回到的品味都将战败。假若指标标识为 塞里alizable,则该目标将被自动种类化,并从一个利用程序域传输至另 三个采取程序域,然后开展反序列化,从而在第二个利用程序域中产生出该目的的二个准确别本。此进程日常号称按值封送。
图片 12
   假若目的是从 MarshalByRefObject 派生得到,则从叁个行使程序域传递至另五个选用程序域的是指标引用,并非目的自己。也得以将 从 MarshalByRefObject 派生获得的靶子标志为 Serializable。远程应用此目的时,担任进行种类化并已先行安插为 SurrogateSelector 的格式化程序将决定体系化进度,并用二个代理替换全体从 MarshalByRefObject 派生获得的对 象。如果未有先行安插为 SurrogateSelector,类别化种类布局将死守上面包车型大巴正经八百体系化准则(请参阅体系化进程的手续)。

 

主题系列化
  要使八个类可系列化,最简易的法子是接受 Serializable 属性对它实行标记,如下所示:

图片 13

1 [Serializable]
2 public class MyObject 
3 {
4    public int n1 = 0;
5    public int n2 = 0;
6    public String str = null;
7 }

图片 14

  以下代码片段表达了怎么着将该类的一个实例连串化为二个文书:

图片 15

1 MyObject obj = new MyObject();
2 obj.n1 = 1;
3 obj.n2 = 24;
4 obj.str = "一些字符串";
5 IFormatter formatter = new BinaryFormatter();
6 Stream stream = new FileStream("MyFile.bin", FileMode.Create,
7 FileAccess.Write, FileShare.None);
8 formatter.Serialize(stream, obj);
9 stream.Close();

图片 16

  本例使用二进制格式化程序开展连串化。您只需创设多个要利用的流和格式化程序的实例,然后调用格式化程序的 塞里alize 方法。流和要连串化的目的实例作为参数提要求此调用。类中的全体成员变量(以至标志为 private 的变量)都将被连串化,但那一点在本例中未明显展现出来。在那点上,二进制体系化差别于只种类化公共字段的 XML 连串化程序。
图片 17
  将对象还原到它原先的情状也特别轻松。首先,创立格式化程序和流以开展读取,然后让格式化程序对指标实行反系列化。以下代码片段表达了什么样进展此操作。

图片 18

 1 IFormatter formatter = new BinaryFormatter();
 2 Stream stream = new FileStream("MyFile.bin", FileMode.Open,
 3 FileAccess.Read, FileShare.Read);
 4 MyObject obj = (MyObject) formatter.Deserialize(fromStream);
 5 stream.Close();
 6 
 7 // 下面是证明
 8 Console.WriteLine("n1: {0}", obj.n1);
 9 Console.WriteLine("n2: {0}", obj.n2);
10 Console.WriteLine("str: {0}", obj.str);

图片 19

  上边所接纳的 BinaryFormatter 效能极高,能生成非常严密的字节流。全数应用此格式化程序种类化的对 象也可接收它实行反类别化,对于连串化将在 .NET 平台上实行反体系化的对象,此格式化程序无疑是三个优越工具。要求注意的是,对指标开展反连串化时 并不调用布局函数。对反连串化增多这项约束,是出于品质方面包车型大巴虚构。但是,那违反了对象编写者常常采纳的局地周转时约定,因而,开辟人士在将对象标志为可 体系化时,应保险思虑了那风度翩翩十分约定。
图片 20
  若是必要全部可移植性,请使用 SoapFormatter。所要做的转移只是将上述代码中的格式化程序换来 SoapFormatter,而 Serialize 和 Deserialize 调用不改变。对于地点使用的亲自过问,该格式化程序将转移以下结果。

图片 21

<SOAP-ENV:Envelope
   xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:SOAP- ENC=http://schemas.xmlsoap.org/soap/encoding/
   xmlns:SOAP- ENV=http://schemas.xmlsoap.org/soap/envelope/
   SOAP-ENV:encodingStyle=
   "http://schemas.microsoft.com/soap/encoding/clr/1.0
  http://schemas.xmlsoap.org/soap/encoding/"
   xmlns:a1="http://schemas.microsoft.com/clr/assem/ToFile">

   <SOAP-ENV:Body>
     <a1:MyObject id="ref-1">
       <n1>1</n1>
       <n2>24</n2>
       <str id="ref-3">一些字符串</str>
     </a1:MyObject>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

图片 22

  供给专心的是,不能继续 Serializable 属性。假设从 MyObject 派生出二个新的类,则那个新的 类也必须利用该属性进行标识,不然将无法类别化。举个例子,尽管准备连串化以下类实例,将交易会示三个 SerializationException,表达 MyStuff 类型未标识为可系列化。

1 public class MyStuff : MyObject
2 {
3    public int n3;
4 }

  使用连串化属性非常便利,但是它存在上述的部分范围。有关曾几何时标识类以举行系列化(因为类编写翻译后就不能再类别化),请参照他事他说加以考查有关注脚(请参阅上边包车型客车类别化准则)。

 

接纳性类别化
   类经常满含不应被连串化的字段。比如,要是有些类用三个分子变量来存款和储蓄线程 ID。当此类被反体系化时,种类化此类时所蕴藏的 ID 对应的线程大概不 再运维,所以对这么些值进行体系化没风趣。能够经过利用 NonSerialized 属性标识成员变量来幸免它们被系列化,如下所示:

图片 23

1 [Serializable]
2 public class MyObject
3 {
4     public int n1;
5     [NonSerialized] public int n2;
6     public String str;
7 }

图片 24

 

自定义种类化
   能够透过在对象上完结 ISerializable 接口来自定义类别化进度。那风度翩翩作用在反类别化后成员变量的值失效时特别有用,可是急需为变量提供值 以重新建立对象的总体气象。要落到实处 ISerializable,需求完成 GetObjectData 方法以致五个特殊的布局函数,在反类别化对象时要用 到此布局函数。以下代码示例表明了何等在前后生可畏部分中提到的 MyObject 类上达成 ISerializable。

图片 25

 1 [Serializable]
 2 public class MyObject : ISerializable
 3 {
 4     public int n1;
 5     public int n2;
 6     public String str;
 7 
 8     public MyObject()
 9     {
10     }
11 
12     protected MyObject(SerializationInfo info, StreamingContext context)
13     {
14         n1 = info.GetInt32("i");
15         n2 = info.GetInt32("j");
16         str = info.GetString("k");
17     }
18 
19     public virtual void GetObjectData(SerializationInfo info,
20                                       StreamingContext context)
21     {
22         info.AddValue("i", n1);
23         info.AddValue("j", n2);
24         info.AddValue("k", str);
25     }
26 }

图片 26

  在系列化进度中调用 GetObjectData 时,需求填写方法调用中提供的 SerializationInfo 对象。只需按名称/值 对的样式充分将在种类化的变量。其名称能够是其他公文。只要已体系化的数额能够在反连串化进度中还原对象,便足以自由选取增多至 SerializationInfo 的成员变量。如若基对象实现了 ISerializable,则派生类应调用其基对象 的 GetObjectData 方法。
图片 27
   需求重申的是,将 ISerializable 增加至某些类时,要求同有时候达成 GetObjectData 以至特种的布局函数。如若缺乏 GetObjectData,编译器将爆发警报。不过,由于不能够强制落成结构函数,所以,匮乏构造函数时不会时有发生警示。假诺在未有结构函数的图景下尝 试反种类化有些类,将会现身卓殊。在拔除潜在安全性和版本调控难点等方面,当前设计优化 SetObjectData 方法。比方,假如将 SetObjectData 方法定义为有些接口的风度翩翩有的,则此格局必得是国有措施,那使得客户只可以编写代码来防止频繁调 用 SetObjectData 方法。能够设想,要是有些对象正在试行有些操作,而某些恶意应用程序却调用此指标的 SetObjectData 方 法,将会挑起部分诡秘的分神。
图片 28
  在反类别化进度中,使用出于此目标而提供的布局函数将 SerializationInfo 传递给类。对象反系列化时,对构造函数的其它可知性限制都将被忽视,由此,能够将类标记为 public、protected、internal 或 private。一个科学的诀假使,在类未封装的气象下,将布局函数标识为 protect。借使类已打包,则应标志为 private。要回涨对象的情状,只需利用种类化时行使的名称,从 SerializationInfo 中搜索变量的值。若是基类完毕了 ISerializable,则应调用基类的构造函数,以使底蕴对象足以还原其变量。
图片 29
  借使从完结了 ISerializable 的类派生出一个新的类,则只要新的类中富含别的要求系列化的变量,就非得同时落实构造函数以致 GetObjectData 方法。以下代码片段彰显了哪些利用上文所示的 MyObject 类来产生此操作。

图片 30

 1 [Serializable]
 2 public class ObjectTwo : MyObject
 3 {
 4     public int num;
 5 
 6     public ObjectTwo() : base()
 7     {
 8     }
 9 
10     protected ObjectTwo(SerializationInfo si, StreamingContext context) :
11         base(si,context)
12     {
13         num = si.GetInt32("num");
14     }
15 
16     public override void GetObjectData(SerializationInfo si,
17                                        StreamingContext context)
18     {
19         base.GetObjectData(si,context);
20         si.AddValue("num", num);
21     }
22 }

图片 31

  切记要在反类别化布局函数中调用基类,不然,将永恒不会调用基类上的布局函数,并且在反连串化后也敬敏不谢塑造生龙活虎体化的对象。
图片 32
   对象被彻底重新构建,然而在反体系化进度中调用方法大概会拉动不良的副作用,因为被调用的章程恐怕援用了在调用时并未有反系列化的目的征引。如若正在进展反类别化的类实现了 IDeserializationCallback,则反体系化整个对象图表后,将自行调用 OnSerialization 方 法。当时,引用的全数子对象均已全然苏醒。有个别类不选用上述事件侦听器,很难对它们进行反系列化,散列表正是三个优越的例证。在反体系化进程中寻找关键字/值对特别轻便,可是,由于不能够作保从散列表派生出的类已反连串化,所以把那一个目的增多回散列表时晤面世部分难点。由此,建议近日不用在散列表上调用方法。
图片 33
系列化进度的步调
  在格式化程序上调用 Serialize 方法时,对象类别化遵照以下法则举行:
图片 34
  检查格式化程序是或不是有代理采取器。假诺有,检查代理选择器是还是不是管理钦定项目标对象。借使选拔器管理此目的类型,就要代理选用器上调用 ISerializable.GetObjectData。
  若无代理选用器或有却不管理此类型,将检查是或不是使用 塞里alizable 属性对目的开展标志。假设未标识,将会掀起 SerializationException。
  借使目的已被科学标志,将检核查象是还是不是落到实处了 ISerializable。假若已贯彻,将要对象上调用 GetObjectData。
  假使目的未兑现 Serializable,将应用暗中同意的连串化计策,对富有未标识为 NonSerialized 的字段都进展类别化。
  版本调节
   .NET 框架扶持版本调节和并列排在一条线实行,并且,假若类的接口保持少年老成致,全数类均可跨版本职业。由于体系化涉及的是成员变量而非接口,所以,在向要跨版本 体系化的类中加多成员变量,或从中删除变量时,应严慎行事。特别是对此未实现 ISerializable 的类更应如此。若当前版本的情形产生了别的变 化(比方增添成员变量、更更动量类型或转移变量名称),都意味倘诺相似种类的现成对象是选取最先版本举办种类化的,则无从得逞对它们实行反系列化。
图片 35
  即使目的的境况须要在差异版本间产生改换,类的审核人能够有二种选用:
图片 36
  实现 ISerializable。那使您能够正确地调控种类化和反体系化进度,在反体系化进度中正确地抬高和分解以往景观。
  使用 NonSerialized 属性标识不根本的积极分子变量。仅当预测类在不一致版本间的生成超级小时,才可接受这一个选项。举例,把二个新变量增添至类的较高版本后,能够将该变量标识为 NonSerialized,以管教该类与开始的黄金时代段时代版本保持卓殊。

体系化准则
   由于类编写翻译后便心有余而力不足种类化,所以在安顿新类时应构思体系化。要求思忖的题目有:是还是不是必需跨应用程序域来发送此类?是或不是要远程应用此类?客户将什么选用此 类?恐怕他们会从自身的类中派生出四个须要系列化的新类。只要有这种或许性,就应将类标记为可体系化。除下列情形以外,最棒将全体类都标记为可体系化:
图片 37
  全数的类都恒久也不会超越应用程序域。假若某个类无需种类化但须要超过应用程序域,请从 MarshalByRefObject 派生此类。
  类存款和储蓄仅适用于其日前实例的特殊指针。比如,假使某些类包括非受控的内部存款和储蓄器或文件句柄,请保管将这一个字段标志为 NonSerialized 或根本不连串化此类。
  有些数据成员满含敏感新闻。在这里种地方下,提出落到实处 ISerializable 并仅连串化所须要的字段。 

转自:

值类型直接存款和储蓄对象,而引用类型存款和储蓄对象之处,在对援引类型实行理并答复制的时候,也只是复制对象的地点。

为何要运用类别化?

一起复制叁个援引类型对象主要有三种方法:

1.额外加多八个构造函数,入参为待复制对象(若是字段为援用类型,须求一而再增添构造函数,那样意况会变的十分复杂。)

    public class Test1
    {
        private int field1;
        private int field2;
        private int field3;
        public Test1()
        { 

        }

        public Test1(Test1 test1)
        {
            this.field1 = test1.field1;
            this.field2 = test1.field2;
            this.field3 = test1.field3;
        }
    }

2.施用类别化反类别化(对质量会有杀伤)

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Test t1 = new Test();
            Console.WriteLine(t1.list.Count);
            Test t2 = (Test)Clone(t1);
            t2.list.Add("");
            Console.WriteLine(t2.list.Count);
            Console.WriteLine(t1.list.Count);
            Console.ReadLine();
        }

        public static object Clone(object obj)
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream ms = new MemoryStream();
            bf.Serialize(ms, obj);
            ms.Position = 0;
            return (bf.Deserialize(ms)); ;
        }
    }

    [Serializable]
    public class Test
    {
        public List<string> list = new List<string>();
    }
}

3.利用反射(测量检验了二个英特网的接口可用,可是对质量杀伤和系列化反类别化万分,而且对代码混淆有早晚影响。   

最器重的三个原因是:

1.      将对象的情状保存在储存媒体中以便能够在这里后再一次创制出完全相通的别本;

2.      按值将指标从贰个使用程序域发送至另七个利用程序域。

举例,连串化可用来在 ASP.NET 中保留会话状态,以致将指标复制到 Windows 窗体的剪贴板中。

它还可用以按值将目标从三个运用程序域远程传递至另一个选取程序域。

正文简单介绍了 Microsoft .NET 中运用的系列化。