C# 类的序列化
2024-03-17 17:33:41

序列化对象

被序列化的类要求用 Serializable 属性标注。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Serializable]
internal class MyObject
{
public int Weight = 0;
[NonSerialized]
private string _name;
private int _secret;
public MyObject(int age, string name, int secret)
{
Age = age;
_name = name;
_secret = secret;
}

public int Age { get; set; }

public string Name => _name;

public override string ToString()
{
return $"Name: {Name}, Age:{Age}, Weight:{Weight}, Secret:{_secret}";
}
}

序列化:

1
2
3
4
5
6
7
8
9
var obj = new MyObject(10, "jack", 80)
{
Weight = 100
};

IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyObj.bin", FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, obj);
stream.Close();

反序列化:

1
2
3
4
5
6
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("MyObj.bin", FileMode.Open, FileAccess.Read, FileShare.Read);
var obj = (MyObject)formatter.Deserialize(stream);
stream.Close();

Debug.WriteLine(obj.ToString());

输入内容:

1
Name: , Age:10, Weight:100, Secret:80

由此可知:

  1. 需要被序列化的类要用 Serializable 属性标注。
  2. 默认会序列化所有字段,除非用 NonSerialized 属性忽略。

自定义序列化

如果需要更精细的控制序列化、反序列化过程,则需要实现 ISerializable 接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 实现 ISerializable 接口的 GetObjectData 方法
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
// 将对象的字段添加到 SerializationInfo 对象中
info.AddValue("Name", Name);
info.AddValue("Age", Age);
}

// 反序列化构造函数
private Person(SerializationInfo info, StreamingContext context)
{
// 从 SerializationInfo 对象中检索字段的值
Name = info.GetString("Name");
Age = info.GetInt32("Age");
}
  • 序列化时会调用 GetObjectData 方法。
  • 反序列化时会调用构造函数,构造函数可以是私有的。

    以下是一些需要实现 ISerializable 接口的情况:
  • 自定义序列化格式:如果想要将对象序列化为特定的格式,而不是默认的二进制格式,可以实现 ISerializable 接口来自行定义序列化和反序列化的过程。
  • 对象包含非序列化成员:如果类包含某些成员,它们不应该被序列化,可以通过实现 ISerializable 来排除这些成员。
  • 对象包含敏感信息:如果对象包含敏感信息,如密码或私钥,你可能希望在序列化过程中对其进行加密或以其他方式进行安全处理。
  • 对象包含复杂的引用关系:某些对象包含复杂的引用关系,可能导致默认的序列化过程出现循环引用或无法正确序列化的问题。通过实现 ISerializable 接口,你可以控制序列化过程,以处理这些引用关系。
  • 特殊构造逻辑:如果你的类有特殊的构造逻辑,需要在反序列化过程中执行,可以通过 ISerializable 接口的构造函数来实现这些逻辑。

反序列化完成通知

某些情况下,我们需要在反序列化完成后完成一些逻辑,那么就可以实现 IDeserializationCallback 接口,以便获得通知。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Serializable]
class MyClass : IDeserializationCallback
{
public int OriginalValue { get; set; }
public int ComputedValue { get; private set; }

public MyClass(int value)
{
OriginalValue = value;
}

// 实现 IDeserializationCallback 接口的回调方法
public void OnDeserialization(object sender)
{
// 在反序列化完成后,重新计算 ComputedValue
ComputedValue = OriginalValue * 2;
}
}