178 lines
4.2 KiB
Markdown
178 lines
4.2 KiB
Markdown
# C# 对象的序列化与反序列化
|
||
|
||
## 1. 什么是序列化?
|
||
|
||
**序列化(Serialization)** 是指将对象的状态信息转换为可以存储或传输的形式(例如字节流、文件、内存数据)的过程。
|
||
反之,**反序列化(Deserialization)** 则是将存储或传输的序列化数据还原为对象的过程。
|
||
|
||
在 C# 中,最常用的方式是通过 `BinaryFormatter` 类来完成二进制序列化与反序列化。
|
||
|
||
---
|
||
|
||
## 2. 核心类与方法
|
||
|
||
- **BinaryFormatter.Serialize(Stream, object)**
|
||
将对象序列化到指定的流中。
|
||
|
||
- **BinaryFormatter.Deserialize(Stream)**
|
||
从指定的流中反序列化对象。
|
||
|
||
|
||
> ⚠️ 注意:从 .NET 5 开始,官方已经不推荐使用 `BinaryFormatter`(存在安全风险),更推荐 `System.Text.Json` 或 `XmlSerializer` 等。但在学习和理解基础时,仍可使用。
|
||
|
||
---
|
||
|
||
## 3. 序列化方式
|
||
|
||
### 3.1 文件流方式
|
||
|
||
通过 **文件流(FileStream)** 将对象直接保存到文件中。
|
||
|
||
```
|
||
csharp
|
||
|
||
using System;
|
||
using System.IO;
|
||
using System.Runtime.Serialization.Formatters.Binary;
|
||
|
||
[Serializable] // 必须标记为可序列化
|
||
public class Person
|
||
{
|
||
public string Name;
|
||
public int Age;
|
||
}
|
||
|
||
class Program
|
||
{
|
||
static void Main()
|
||
{
|
||
Person p = new Person { Name = "Tom", Age = 25 };
|
||
|
||
// 序列化到文件
|
||
using (FileStream fs = new FileStream("person.dat", FileMode.Create))
|
||
{
|
||
BinaryFormatter formatter = new BinaryFormatter();
|
||
formatter.Serialize(fs, p);
|
||
}
|
||
|
||
Console.WriteLine("对象已序列化到文件 person.dat");
|
||
}
|
||
}
|
||
|
||
```
|
||
### 3.2 内存流方式
|
||
|
||
通过 **内存流(MemoryStream)** 将对象序列化成字节数组,便于网络传输或自定义存储。
|
||
|
||
```
|
||
csharp
|
||
|
||
using System;
|
||
using System.IO;
|
||
using System.Runtime.Serialization.Formatters.Binary;
|
||
|
||
[Serializable]
|
||
public class Person
|
||
{
|
||
public string Name;
|
||
public int Age;
|
||
}
|
||
|
||
class Program
|
||
{
|
||
static void Main()
|
||
{
|
||
Person p = new Person { Name = "Alice", Age = 30 };
|
||
byte[] buffer;
|
||
|
||
// 序列化到内存流
|
||
using (MemoryStream ms = new MemoryStream())
|
||
{
|
||
BinaryFormatter formatter = new BinaryFormatter();
|
||
formatter.Serialize(ms, p);
|
||
|
||
buffer = ms.ToArray(); // 获取字节数组
|
||
}
|
||
|
||
// 可以将字节数组写入文件或通过网络传输
|
||
File.WriteAllBytes("person_bytes.dat", buffer);
|
||
|
||
Console.WriteLine("对象已序列化为字节数组并写入 person_bytes.dat");
|
||
}
|
||
}
|
||
|
||
```
|
||
---
|
||
|
||
## 4. 反序列化
|
||
|
||
### 4.1 从文件反序列化
|
||
|
||
```
|
||
csharp
|
||
|
||
using (FileStream fs = new FileStream("person.dat", FileMode.Open))
|
||
{
|
||
BinaryFormatter formatter = new BinaryFormatter();
|
||
Person p = (Person)formatter.Deserialize(fs);
|
||
|
||
Console.WriteLine($"Name: {p.Name}, Age: {p.Age}");
|
||
}
|
||
```
|
||
|
||
### 4.2 从字节数组反序列化
|
||
|
||
```
|
||
csharp
|
||
|
||
byte[] buffer = File.ReadAllBytes("person_bytes.dat");
|
||
|
||
using (MemoryStream ms = new MemoryStream(buffer))
|
||
{
|
||
BinaryFormatter formatter = new BinaryFormatter();
|
||
Person p = (Person)formatter.Deserialize(ms);
|
||
|
||
Console.WriteLine($"Name: {p.Name}, Age: {p.Age}");
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 注意事项
|
||
|
||
- **必须添加 `[Serializable]` 特性**
|
||
否则对象无法被序列化。
|
||
|
||
- **字段级别的控制**
|
||
如果某些字段不想被序列化,可以使用 `[NonSerialized]` 特性修饰。
|
||
|
||
```
|
||
csharp
|
||
|
||
[NonSerialized]
|
||
private string password;
|
||
|
||
```
|
||
|
||
- **安全问题**
|
||
`BinaryFormatter` 在反序列化时可能存在安全漏洞,不适合用于处理不受信任的数据。
|
||
|
||
- **替代方案**
|
||
|
||
- 文本序列化(如 `JsonSerializer`、`XmlSerializer`)
|
||
|
||
- 跨平台高性能方案(如 `MessagePack`、`Protobuf`)
|
||
|
||
|
||
---
|
||
|
||
## 6. 总结
|
||
|
||
- **文件流序列化**:直接将对象保存到磁盘文件中。
|
||
|
||
- **内存流序列化**:对象 → 字节数组 → 可保存或传输。
|
||
|
||
- **反序列化**:根据文件流或字节数组还原对象。
|
||
|
||
|
||
序列化与反序列化是 C# 中对象持久化与数据传输的核心知识点,实际开发中需结合安全性与场景选择合适的序列化方式。 |