学习总结——关于C#中的序列化

2022-01-18 11:15:41 浏览数 (1)

1、什么是序列化

(来自百度百科)

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

确切地说,代码执行序列化需要特殊的权限:即指定了 SerializationFormatter 标志的 SecurityPermission。在默认策略下,通过 Internet 下载的代码或 Internet 代码不会授予该权限;只有本地计算机上的代码才被授予该权限。序列化使其他代码可以查看或修改那些不序列化便无法访问的对象实例数据。

2、c#中的序列化

备注(转载自https://www.cnblogs.com/gc2013/p/4070474.html)

①基本序列化

要使一个类可序列化,最简单的方法是使用 Serializable 属性对它进行标记,如下所示

代码语言:c#复制
[Serializable]
代码语言:c#复制
public class MyObject
代码语言:c#复制
{
   public int n1 = 0;
   public int n2 = 0;
   public String str = null;
}

将上面的类的一个实例序列化为一个文件

代码语言:c#复制
MyObject obj = new MyObject();
代码语言:c#复制
obj.n1 = 1;
代码语言:c#复制
obj.n2 = 24;
代码语言:c#复制
obj.str = "一些字符串";
代码语言:c#复制
IFormatter formatter = new BinaryFormatter();
代码语言:c#复制
Stream stream = new FileStream("MyFile.bin", FileMode.Create,
代码语言:c#复制
FileAccess.Write, FileShare.None);
代码语言:c#复制
formatter.Serialize(stream, obj);
代码语言:c#复制
stream.Close();

上面实例的反序列化

代码语言:c#复制
IFormatter formatter = new BinaryFormatter();
代码语言:c#复制
Stream stream = new FileStream("MyFile。bin", FileMode.Open,
代码语言:c#复制
FileAccess.Read, FileShare.Read);
代码语言:c#复制
MyObject obj = (MyObject) formatter.Deserialize(fromStream);
代码语言:c#复制
stream.Close();

如果要求具有可移植性,请使用 SoapFormatter。所要做的更改只是将以上代码中的格式化程序换成 SoapFormatter,而 Serialize 和 Deserialize 调用不变。

需要注意的是,无法继承 Serializable 属性。如果从 MyObject 派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个 SerializationException,说明 MyStuff 类型未标记为可序列化。

②选择性序列化

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

代码语言:c#复制
[Serializable]
代码语言:c#复制
public class MyObject
代码语言:c#复制
{
   public int n1;
   [NonSerialized]
   public int n2;
   public String str;
}

③自定义序列化

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

代码语言:c#复制
[Serializable]
代码语言:c#复制
public class MyObject : ISerializable
代码语言:c#复制
{
   public int n1;
   public int n2;
   public String str;

   public MyObject()
   {
   }

   protected MyObject(SerializationInfo info, StreamingContext context)
   {
     n1 = info.GetInt32("i");
     n2 = info.GetInt32("j");
     str = info.GetString("k");
   }

   public virtual void GetObjectData(SerializationInfo info,
StreamingContext context)
   {
     info.AddValue("i", n1);
     info.AddValue("j", n2);
     info.AddValue("k", str);
   }
}

在序列化过程中调用 GetObjectData 时,需要填充方法调用中提供的 SerializationInfo 对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至 SerializationInfo 的成员变量。如果基对象实现了 ISerializable,则派生类应调用其基对象的 GetObjectData 方法。

需要强调的是,将 ISerializable 添加至某个类时,需要同时实现 GetObjectData 以及特殊的构造函数。如果缺少 GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于 SetObjectData 方法。例如,如果将 SetObjectData 方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用 SetObjectData 方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的 SetObjectData 方法,将会引起一些潜在的麻烦。

在反序列化过程中,使用出于此目的而提供的构造函数将 SerializationInfo 传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为 public、protected、internal或 private。一个不错的办法是,在类未封装的情况下,将构造函数标记为 protect。如果类已封装,则应标记为 private。要还原对象的状态,只需使用序列化时采用的名称,从 SerializationInfo 中检索变量的值。如果基类实现了 ISerializable,则应调用基类的构造函数,以使基础对象可以还原其变量。

如果从实现了 ISerializable 的类派生出一个新的类,则只要新的类中含有任何需要序列化的变量,就必须同时实现构造函数以及 GetObjectData 方法。以下代码片段显示了如何使用上文所示的 MyObject 类来完成此操作。

代码语言:c#复制
[Serializable]
代码语言:c#复制
public class ObjectTwo : MyObject
代码语言:c#复制
{
   public int num;

   public ObjectTwo() : base()
   {
   }

   protected ObjectTwo(SerializationInfo si, StreamingContext context) :
base(si,context)
   {
     num = si.GetInt32("num");
   }

   public override void GetObjectData(SerializationInfo si,
StreamingContext context)
   {
     base.GetObjectData(si,context);
     si.AddValue("num", num);
   }
}

切记要在反序列化构造函数中调用基类,否则,将永远不会调用基类上的构造函数,并且在反序列化后也无法构建完整的对象。

在反序列化过程中检索关键字/值对非常容易,但是,由于无法保证从散列表派生出的类已反序列化,所以把这些对象添加回散列表时会出现一些问题。因此,建议目前不要在散列表上调用方法。

0 人点赞