1. Введение
想象一下,你程序里的一个对象就像一个很复杂、很漂亮的 LEGO 拼搭模型。它由很多零件组成,每个零件都在自己位置上,整体就是一个完整的东西。只要拼搭摆在你桌子上(在电脑内存里),一切都很好。但如果你要把它送到朋友家(通过网络传输),或者收进盒子里留到下次(保存到磁盘)怎么办?你不能就那样把它直接放进扁平的盒子里!那会把它弄坏的!
这正是序列化要做的事——把对象小心拆成零件以便安全地存储或传输。 这是一个把你“活着”在内存中的对象转换成字节序列(或者文本表示)的过程,这样就可以保存到文件、通过网络发送,或放进剪贴板。按本质来说,就像把 LEGO 小心拆解,把零件分包、贴好标签,然后装箱运走或保存起来。
flowchart LR
ObjectInMemory(内存中的对象)
Serialize[序列化]
BytesOrText[字节 / 文本文件 / JSON / XML]
Deserialize[反序列化]
ObjectAgain(对象又回到内存)
ObjectInMemory -->|serialize| Serialize
Serialize --> BytesOrText
BytesOrText -->|deserialize| Deserialize
Deserialize --> ObjectAgain
当然,既然我们打包了东西,就得会解包!逆过程叫做 反序列化。就是你把那箱装着分包的 LEGO 倒出来,然后按说明(或凭记忆)把它们组装回去,把对象在内存中恢复到打包前的那个状态。就像魔法一样!
“序列化”这个词字面意思是“按顺序的表示”。我们把复杂的、可能在内存中很分散的结构(对象可能引用其他对象、集合等,形成一个完整的“对象图”)变成线性的、连续的形式,这样就容易写入或传输了。
2. Почему недостаточно просто File.WriteAllText?
好问题!如果所有数据都是简单的字符串或数字,确实不需要序列化。但在现实中,我们的应用处理的是复杂模型:客户、订单、商品、学生、英雄、关卡。这些东西都是内存中的对象。这就是为什么序列化是你的好朋友:
保存应用状态
想象你在写一个文本编辑器。用户输入文字、改字体、插入图片。所有这些数据(文本、设置、光标位置)在程序里就是对象。当用户点击“保存”时,你希望下次打开程序时看到的一切都和离开时一样。序列化可以把需要的对象“冻结”并写到磁盘,启动时再“解冻”恢复。这就像游戏里的“保存进度”。你不会希望每次断电都得从头再玩一遍吧?
应用间的数据交换
你的 C# 程序可能要和一个用 Python 写的 web 服务器通信。或者你的移动应用(比如用 Xamarin 或 MAUI)要和 .NET 的服务器交互。这些程序没有共享内存。要交换数据,需要一个双方都能理解的通用格式。序列化把对象变成这个通用格式(比如 JSON 或 XML),可以通过网络传输。另一端接收后反序列化成各自语言能理解的对象。没有序列化,你就得手动“拆包”每一块数据再组装起来——既慢又容易出错!
想象你给外国寄包裹。你不能就按自己的方式乱塞东西;你得按国际规则包装(序列化)。对方收到后才能打开容器取出物品(反序列化)。
应用配置
很多应用有大量设置:窗口大小、文件路径、最近打开的文档、配色方案。把每个设置都单独存在变量里,然后手动写进设置文件(比如 .ini 或 .txt)既麻烦又笨。更简单的做法是定义一个类 应用设置(НастройкиПриложения 原意),每个设置作为属性,然后直接把整个设置对象序列化到文件。启动时再反序列化。很舒服!
数据缓存
有时候获取数据(比如从数据库或远程服务器)很耗时。为避免每次都请求,可以把结果获取一次后序列化并保存到缓存(磁盘或专门存储)。下次请求时先检查缓存,如果有就反序列化并返回。这能显著加速应用并减轻外部数据源的负担。
3. Основная идея: сохранение "состояния"
序列化最核心的概念是 保存“状态”。对象本质上是它字段及其在某一时刻的值的集合。序列化就是给这个状态拍张“快照”。反序列化时,我们不是仅仅创建一个新的空对象,而是用序列化时的字段值把它复原。
这不仅仅是拷贝数据,而是对结构和数值的深度复制,包括对其他对象的引用(前提是序列化器支持)。有些序列化器甚至会保存对象的类型信息,这样就能在反序列化时把它恢复成正确的类,即使在反序列化时我们事先不知道确切类型也没问题。
Классический пример: сериализация питомцев
继续我们“宠物百科”应用。假设有下面这样的类:
public class Pet
{
public string Name { get; set; }
public string Type { get; set; } // 例如: "猫", "狗"
public int Age { get; set; }
}
我们的目标:
- 在内存里填充这样对象的集合,
- 把它保存到文件,
- 然后在之后恢复(比如程序重启后)。
示意图大致长这样:
集合 List<Pet>
↓ 序列化
文件 (JSON, XML, 字节)
↓ 反序列化
集合 List<Pet>(又回到内存!)
4. Какие бывают форматы сериализации
就像运输里有不同类型的容器(纸箱、木箱、金属集装箱),编程里也有各种各样的序列化格式。每种都有自己的特点、优点和缺点。
这里先不深入实现细节,只记住几个名字,方便后面课程里能理解在讲什么:
- JSON (JavaScript Object Notation):现在可能是最流行的格式。它是文本的,比较容易被人读取(相对来说),在 web 数据交换中被广泛使用。表现为“键-值”对,用大括号包起来。本质上就是结构化的文本,程序解析起来很方便。
- XML (Extensible Markup Language):更早期但仍然常见的文本格式。基于标签,像 HTML,有严格的结构。常用于配置文件和企业系统间的数据交换。也能被人读懂,但通常比较啰嗦。
- 二进制格式:这些格式不把数据保存为文本,而是直接以字节的形式保存,和内存里的存储更接近。它们通常更紧凑、序列化/反序列化更快,但对人类不可读。适合追求性能或文件大小极限的场景,比如游戏存档,或者在系统内部大量数据传输。
选哪个格式取决于任务:是否需要人来读数据?速度和大小重要吗?要传到哪些平台?用于不同系统间交换数据时,JSON 和 XML 往往更通用。对同一程序内部保存数据,二进制格式可能更快更高效。
如你所见,序列化是一个强大的工具,支撑着很多现代应用。它让程序能在重启间“记住”数据、互相通信并高效管理复杂信息。后面的课程我们不再只说“魔法”,而是真正动手,用 .NET 的库来实现这些功能!准备好,挺有意思的!
GO TO FULL VERSION