【C#】[Serializable]的作用 序列化--01

发布时间 2024-01-08 21:11:46作者: VisionCode

定义:序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象(百度百科)

序列化的目的:1、以某种存储形式使自定义对象持久化;2、将对象从一个地方传递到另一个地方。3、使程序更具维护性。

技术:

* 二进制序列化保持类型保真度,这对于在应用程序的不同调用之间保留对象的状态很有用。例如,通过将对象序列化到剪贴板,可在不同的应用程序之间共享对象。您可以将对象序列化到流、磁盘、内存和网络等等。远程处理使用序列化“通过值”在计算机或应用程序域之间传递对象。(百度百科)

* XML 序列化仅序列化公共属性和字段,且不保持类型保真度。当您要提供或使用数据而不限制使用该数据的应用程序时,这一点是很有用的。由于 XML 是一个开放式标准,因此,对于通过 Web 共享数据而言,这是一个很好的选择。SOAP 同样是一个开放式标准,这使它也成为一个颇具吸引力的选择。(百度百科)

例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。反之,反序列化根据流重新构造对象。

实现:在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本

实质:序列化就是把内存中对象以一种可以保存的形式保存起来。

原因:将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本;按值将对象从一个应用程序域发送至另一个应用程序域。

Serializable特性的作用

该标签指示一个类可以序列化。 便于在网络中传输和保存

但是像WCF,即使你不去标记,它也会花时间去反射字段。它已经不在乎SerializableAttribute了。
需要注意的是:
序列化可以制定序列化整个类,或者 类的某些属性。

 

总结:

序列化整个类使用Serializable标记,部分属性不序列化使用NonSerialized

创建本地文件流存储数据

FileStream fs = new FileStream(fileFullName, FileMode.Open, FileAccess.ReadWrite)

或本地内存流存储数据

 MemoryStream stream = new MemoryStream( );

使用 XmlSerializer类序列化

  1.  
    XmlSerializer xs = new XmlSerializer(obj.GetType());
  2.  
    //序列化成Xml文件.
  3.  
    xs.Serialize(fs, obj);

使用 BinaryFormatter类序列化

  1.  
    BinaryFormatter bf = new BinaryFormatter();
  2.  
    bf.Serialize(fs, obj);

自定义序列化

实现 ISerializable,需要实现 GetObjectData方法以及一个特殊的构造函数

你是想数据持久化吗?
首先,如果Rectangle是你自定义的类的话,必须保证这个类里面的所有状态都是可以储存的,并且要有[Serializable()]标识.
然后你可以用 XmlSerializer类或者 BinaryFormatter类将其逐一序列化.序列化后。
可以将其序列化成XML存进数据库也可以序列化成2进制保存.

==========================
首先,你想保存某个类中的数据,那这个类最好是个实体类,
比如

  1.  
    [Serializable]
  2.  
    [XmlRoot("Rectangle")]
  3.  
    public class Rectangle{
  4.  
    //fields here.
  5.  
    ...
  6.  
    public Rectangele(){...}
  7.  
    [XmlElement("Width")]
  8.  
    public int Width{get;set;}
  9.  
    [XmlElement("Length")]
  10.  
    public int Length{get;set;}
  11.  
    }

像这样的类被选择序列化才比较有意义,它要求必须有一个无参数的构造函数.
经过类似下面的方法序列化后,将获得一个文件流

  1.  
    Rectangle rec = new Rectangle{Width = 5,Length = 6};
  2.  
    using (FileStream fs = new FileStream(fileFullName, FileMode.Create, FileAccess.Write, FileShare.Read))
  3.  
    {
  4.  
    XmlSerializer xs = new XmlSerializer(rec.GetType());
  5.  
    //序列化成Xml文件.
  6.  
    xs.Serialize(fs, rec);
  7.  
    //使用文件流
  8.  
    ...
  9.  
    }
同样可以序列化成 2进制文件流.:
  1.  
    using (FileStream fs = new FileStream(fileFullName, FileMode.Open, FileAccess.ReadWrite))
  2.  
    {
  3.  
    BinaryFormatter bf = new BinaryFormatter();
  4.  
    bf.Serialize(fs, rec);
  5.  
    }

另外,如果你的类用来记录状态,比如是否跟远程主机连接成功,之类的,那么序列化是没有意义的.因为当你反序列化的时候,他的环境已经变了,而以前保存的状态已经不稳定了.

序列化,是记录类中所有可序列化的信息.并可以还原.
另外一个选择呢,就是只记录内容,用内容重新实例化类:
比如

Rectangle rec = new Rectangle{Width = 5,Length = 6};

rec 你只需要在数据库中记录Width = 5,Length = 6
在将来,你可以根据 Width,Length的值重新实例化Rectangle类.
至于集合,可类推.

选择性序列化
 类通常包含不应被序列化的字段。例如,假设某个类用一个成员变量来存储线程 ID。当此类被反序列化时,序列化此类时所存储的 ID 对应的线程可能不再运行,所以对这个值进行序列化没有意义。可以通过使用 NonSerialized 属性标记成员变量来防止它们被序列化,如下所示:

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

 

 自定义序列化
 可以通过在对象上实现 ISerializable 接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。要实现 ISerializable,需要实现 GetObjectData方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。以下代码示例说明了如何在前一部分中提到的 MyObject 类上实现 ISerializable。

 
  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.  
    }

 在序列化过程中调用 GetObjectData 时,需要填充方法调用中提供的 SerializationInfo 对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至 SerializationInfo 的成员变量。如果基对象实现了 ISerializable,则派生类应调用其基对象的 GetObjectData 方法。
 
 需要强调的是,将 ISerializable 添加至某个类时,需要同时实现 GetObjectData 以及特殊的构造函数。如果缺少 GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于 SetObjectData 方法。例如,如果将 SetObjectData 方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用 SetObjectData 方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的 SetObjectData 方法,将会引起一些潜在的麻烦。

NonSerialized 属性标记成员变量来防止它们被序列化

序列化过程的步骤

在格式化程序上调用 Serialize 方法时,对象序列化按照以下规则进行:

检查格式化程序是否有代理选取器。如果有,检查代理选取器是否处理指定类型的对象。如果选取器处理此对象类型,将在代理选取器上调用 ISerializable.GetObjectData。
如果没有代理选取器或有却不处理此类型,将检查是否使用 Serializable 属性对对象进行标记。如果未标记,将会引发 SerializationException。
如果对象已被正确标记,将检查对象是否实现了 ISerializable。如果已实现,将在对象上调用 GetObjectData。
如果对象未实现 Serializable,将使用默认的序列化策略,对所有未标记为 NonSerialized 的字段都进行序列化。

根据序列化和反射深拷贝的实现

  1.  
    /// <summary>
  2.  
        /// 序列化和反序列化辅助类
  3.  
        /// </summary>
  4.  
    public class SerializableHelper
  5.  
    {
  6.  
    public string Serializable<T>(T target)
  7.  
    {
  8.  
    using (MemoryStream stream = new MemoryStream())
  9.  
    {
  10.  
    new BinaryFormatter().Serialize(stream, target);
  11.  
     
  12.  
    return Convert.ToBase64String(stream.ToArray());
  13.  
    }
  14.  
    }
  15.  
     
  16.  
    public T Derializable<T>(string target)
  17.  
    {
  18.  
    byte[] targetArray = Convert.FromBase64String(target);
  19.  
     
  20.  
    using (MemoryStream stream = new MemoryStream(targetArray))
  21.  
    {
  22.  
    return (T)new BinaryFormatter().Deserialize(stream);
  23.  
    }
  24.  
    }
  25.  
     
  26.  
    public T Clone<T>(T target)
  27.  
    {
  28.  
    using (MemoryStream stream = new MemoryStream())
  29.  
    {
  30.  
    new BinaryFormatter().Serialize(stream, target);
  31.  
    using (MemoryStream instream = new MemoryStream(stream.ToArray()))
  32.  
    {
  33.  
    return (T)new BinaryFormatter().Deserialize(instream);
  34.  
    }
  35.  
     
  36.  
    }
  37.  
    }
  38.  
    }
  1.  
    1 MemoryStream stream = new MemoryStream() //创建一个流,其后备存储为内存。
  2.  
    2 new BinaryFormatter().Serialize(stream, target) // 以二进制格式序列化和反序列化对象。
  3.  
    3 Convert.ToBase64String(stream.ToArray()); //轉換為使用 Base-64 位數編碼的相等字串表示
  4.  
    BinaryFormatter 序列化与反序列的对象
  5.  
    序列化: 1 把对象保存到内存流 2把内存流转化为字节 3把字节转化为字符串
  6.  
    反序列化 1 把字符串转化为字节 2 把字节转换为内存流 3 把内存流转化为对象

简单总结知识点:

定义:

序列化:将对象转换为可以存储或传输的形式的过程。

实现:对象将状态写入到存储区。以后,在存储区中读取对象的状态,重新创建该对象

序列化的目的:

1、对象持久化;2、传递对象。

技术:

* 二进制序列化保持类型保真度,保留对象的状态很有用。例如,剪贴板,序列化到磁盘、内存和网络等。

* XML 序列化仅序列化公共属性和字段,且不保持类型保真度。当提供数据有用

例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。反之,反序列化根据流重新构造对象。

实质:序列化就是把内存中对象以一种可以保存的形式保存起来。

原因:创建出相同的副本;将对象发送至另一个应用程序域。

Serializable特性的作用

该标签指示一个类可以序列化。 便于在网络中传输和保存

但是像WCF,即使你不去标记,它也会花时间去反射字段。它已经不在乎SerializableAttribute了。
需要注意的是:
序列化可以制定序列化整个类,或者 类的某些属性。

序列化整个类使用Serializable标记,部分属性不序列化使用NonSerialized(无法对公有属性使用)

自定义序列化

实现 ISerializable,需要实现 GetObjectData方法以及一个特殊的构造函数