CodeGym /课程 /C# SELF /操作步骤和例子:对象序列化

操作步骤和例子:对象序列化

C# SELF
第 43 级, 课程 1
可用

1. 介绍

我们已经习惯在程序里创建对象。记得吗,我们很快就要更深入地学习类和对象,但现在我们已经能理解,变量、列表、甚至简单的字符串不是随便的东西,它们在程序里是某种“实体”。比如我们可以创建一个变量 int age = 30; 或者 string name = "瓦西娅";. 但是,如果我们需要把一个完整的用户信息保存到文件,用户有名字、年龄、地址、喜欢的书单等等,该怎么办?

想象一下:你在写游戏。你有一个 Player 对象,里面有很多属性:health、level、inventory(物品列表)、地图坐标等等。玩家玩着玩着,升级、捡到神器。然后他决定退出游戏。会发生什么?保存在内存里的所有数据都会消失!太伤心了!为了不让这事发生,我们需要把 Player 对象的状态保存到文件,当玩家回来时再把它加载回来。

这时候就轮到序列化出场了。它就是在内存里的“活”对象和磁盘上“死但持久”的数据之间搭桥的东西。

2. 序列化过程(从对象到文件)

我们来拆解一下把对象变成可以写入文件的字节流的“魔法”过程。

假设我们有这样一个类 Book(书籍):


// 这是我们创建 Book 对象的“蓝图”或“模板”
public class Book
{
    // 书的属性
    public string Title { get; set; } // 书名
    public string Author { get; set; } // 作者
    public int Year { get; set; } // 出版年份

    // 构造函数 - 用来创建新的 Book 对象
    public Book(string title, string author, int year)
    {
        Title = title;
        Author = author;
        Year = year;
    }

    // 用于方便输出书信息的方法(对序列化不是必须,但很有用)
    public void DisplayInfo()
    {
        Console.WriteLine($"名称: {Title}, 作者: {Author}, 年份: {Year}");
    }
}

步骤 1:创建要序列化的对象。

首先,当然要有一个我们想保存的对象。例如,我们创建了一个 Book 的实例:

Book myFavoriteBook = new Book("银河系漫游指南", "道格拉斯·亚当斯", 1979);

这个对象 myFavoriteBook 目前存在于内存中。

步骤 2:选择工具(序列化器)。

我们不能直接把对象“拷贝”到磁盘上。计算机不能直接在文件里理解对象——它需要字节。需要一个专门的工具——序列化器。它的任务是把我们的对象分解成组成部分(它的属性: Title, Author, Year)并把这些部分转成字节序列或文本(比如 JSON 或 XML)。

今天我们不深入具体实现——只把它当成一个“转换盒子”来理解。

步骤 3:把对象转换成数据流。

序列化器拿到我们的对象 myFavoriteBook,查看它的属性(Title, Author, Year)并把每个属性转换成可以写入的格式。所有这些字节(或文本字符)被拼成一条连续的数据流——一条“信息带”。

步骤 4:把数据流写入文件。

现在有了这条“信息带”,我们使用熟悉的 FileStream,如果是文本格式(比如 JSON)可能还会用 StreamWriter,或者直接用 FileStream(用于纯二进制数据),把这条数据写到磁盘上。

3. 反序列化过程(从文件到对象)

步骤 1:从文件读取数据流。

我们再次使用 FileStream,必要时用 StreamReader(如果是文本格式),来读取文件内容。数据以字节流或文本“带”的形式返回。

步骤 2:选择工具(反序列化器)。

需要一个相反的工具——反序列化器。它需要知道如何解释收到的字节/文本并正确重建对象结构。很重要的一点:通常反序列化要用和序列化相同类型的序列化器(通常也是同一个库),否则你的“组合说明”可能无法被正确理解。

步骤 3:把数据流转换回对象。

反序列化器读取数据,知道哪里是 Title,哪里是 Author,哪里是 Year,并据此创建一个新的 Book 对象,填充它的属性。

步骤 4:得到可用的对象。

好了!我们又有了一个完整的 Book 对象在内存里,可以继续使用。

示例:手动保存我们的“超棒书”(为了理解概念)

暂且不使用专门的库:用最简单的“手动”序列化和反序列化,借助 StreamWriter 和 StreamReader。这能帮助理解基本原理。

我们的对象 Book:

public class Book
{
    public string Title { get; set; }
    public string Author { get; set; }
    public int Year { get; set; }

    public Book(string title, string author, int year)
    {
        Title = title;
        Author = author;
        Year = year;
    }

    public void DisplayInfo()
    {
        Console.WriteLine($"名称: \"{Title}\", 作者: {Author}, 年份: {Year}");
    }
}

手动序列化:方法 SaveBookToTextFile

创建一个方法,把书的属性逐行保存到文本文件。

void SaveBookToTextFile(Book book, string filePath)
{
    using var writer = new StreamWriter(filePath);
    writer.WriteLine(book.Title);
    writer.WriteLine(book.Author);
    writer.WriteLine(book.Year);
}

发生了什么?我们打开了 StreamWriter,按顺序写入 Title, Author, Year —— 这就是最简单的序列化方案。

如果运行代码,文件内容会像这样:


银河系漫游指南
道格拉斯·亚当斯
1979

手动反序列化:方法 LoadBookFromTextFile

写一个方法,读取数据并组装成新的 Book 对象。

Book LoadBookFromTextFile(string filePath)
{
    using var reader = new StreamReader(filePath);
    string title = reader.ReadLine();
    string author = reader.ReadLine();
    int year = int.Parse(reader.ReadLine());
    return new Book(title, author, year);
}

并在 Main 中使用这些方法:

// 创建对象
var myBook = new Book("银河系漫游指南", "道格拉斯·亚当斯", 1979);
string filePath = "my_favorite_book.txt";

// 把它保存到文件
SaveBookToTextFile(myBook, filePath);

// 从文件读取
Book loadedBook = LoadBookFromTextFile(filePath);

在 LoadBookFromTextFile 中发生了什么?我们打开 StreamReader 并按相同顺序读取行:先是标题,再是作者,最后把年份通过 int.Parse 转成整数。然后我们用这些值创建一个新的 Book 实例。

在实际代码中,应该加上检查(比如 File.Exists)和用 try-catch 做错误处理,但这里我们专注于核心思路。

为什么“手动”序列化不太好(以及为什么需要库)?

  • 手动代码多。 每个属性都要显式写入和读取。如果对象和字段很多,代码会膨胀。
  • 对修改很脆弱。 增加了新属性 Pages(int)?你得同时改写入和读取,还要严格保证顺序。
  • 复杂结构难处理。 嵌套集合和对象(比如 List<Chapter>)会把代码搞成“意大利面”式的混乱。
  • 格式与效率。 文本格式简单但不总是紧凑或安全;处理二进制时需要手动管理字节、用 BinaryWriter/BinaryReader 等。
  • 没有元数据。 文件本身不知道第一行是 Title、第三行是 Year。专用的序列化器可以保存元数据,对模型变化更有韧性。

这就是为什么在真实项目里我们会用现成的序列化库,它们能自动解析/组装对象,支持 JSON、XML 和二进制格式,并能更稳健地应对模型变化。下节课我们会继续讲这个内容!

实际应用:序列化的用途

  • 保存和加载数据。 设置、游戏进度、配置。
  • 通过网络传输数据。 服务之间交换复杂对象(经常用 JSON)。
  • 缓存。 快速重用之前获取的数据。
  • 记录复杂对象的日志。 方便调试和审计。
  • 深拷贝对象。 用序列化+反序列化作为克隆对象图的一种方式。

序列化是现代软件的基石之一:从保存游戏进度到和 web 服务打交道,它无处不在!

2
任务
C# SELF, 第 43 级, 课程 1
已锁定
创建一个简单的保存系统
创建一个简单的保存系统
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION