1. 介紹
在很多舊的 C# 教材和範例中,你會看到用 BinaryFormatter 做序列化。它曾經是標準:方便、速度快、麻煩少。舊時的範例大概長這樣:
// 危險的舊寫法!別在新專案這樣做!
var bf = new BinaryFormatter();
using (var stream = File.Open("player.dat", FileMode.Create))
{
bf.Serialize(stream, playerObject);
}
但在現代的 .NET 9 你已經連編譯這段程式碼都做不到:BinaryFormatter 已經從平台中移除。
從英雄到被淘汰
最初 BinaryFormatter 是設計成通用的 .NET 物件序列化/反序列化機制,可以把複雜的物件圖「如實」保存。隨著時間推移,暴露出嚴重的安全與相容性問題,現在它已成為過去式 — 有點像串流時代的錄音帶機,被淘汰是必然的。
2. 拒用 BinaryFormatter 的主要原因
超大的安全漏洞
BinaryFormatter 不只序列化資料,還會從輸入流還原型別。如果不加檢查地反序列化不受信任的資料(例如從網路收到的),攻擊者可以植入載荷,導致遠端執行程式碼(RCE)。這就是為什麼 Microsoft 多年來一直警告:「不要使用 BinaryFormatter」。從 .NET 5 開始它被標註為過時且危險([Obsolete]),到 .NET 9 則被移除。
依賴類別的內部結構
BinaryFormatter 的格式會帶入型別的實作細節:欄位、封裝與版本。類別一旦稍微改動(新增或移除欄位),就可能破壞與既有檔案的相容性。這讓它不適合用於長期儲存或跨版本交換資料。
跨平台相容性的困難
用 BinaryFormatter 存的資料綁定在 .NET 物件的內部表示上。要在其他平台/語言,甚至不同版本的 .NET 上讀取這些資料通常是不可能的。
3. 如何發現「舊程式碼」以及該怎麼做?
以下是常見使用 BinaryFormatter 的徵兆:
using System.Runtime.Serialization.Formatters.Binary; // <-- 可疑!
BinaryFormatter bf = new BinaryFormatter(); // <-- 危險!
bf.Serialize(...);
bf.Deserialize(...);
在現代的 .NET 版本中這段程式碼無法編譯:工具會報錯像是 "類型或名稱未找到"。在遺留的 .NET Framework 專案中通常至少會有嚴重的警告。
如果你發現這類程式碼,就把 BinaryFormatter 換成現代且安全的替代方案。
今天該用哪些格式和類別?
對於 JSON
- System.Text.Json — 快、較安全,且內建在 .NET 裡。官方文件
- Newtonsoft.Json — 廣泛使用的第三方函式庫,適合較複雜的情境。
對於 XML
- XmlSerializer — 用於 XML 序列化。文件
對於二進位格式
- System.Formats.Cbor (CBOR)
- Protobuf.Net (Protocol Buffers), MessagePack for C#
針對特殊需求選擇專門的格式與函式庫 — 已經沒有像 BinaryFormatter 那種「萬用」的二進位序列化器,這其實是件好事。
4. 把使用 BinaryFormatter 的程式碼改寫
哪些情況不能用 BinaryFormatter
別用於:
- 儲存使用者設定和重要資料。
- 透過網路傳輸資料(Client ↔ Server)。
- 反序列化來自不受信任來源的資料。
建議改用:
- 日常需求 — System.Text.Json。
- 設定檔 — JSON、XML 等。
- 跨語言交換 — 使用跨平台相容的格式(JSON、XML、Protobuf、MessagePack)。
現在該寫什麼程式碼?
序列化成 JSON(推薦做法)
using System.Text.Json;
// 你的類別:
public class Player
{
public string Name { get; set; } = "";
public int Health { get; set; }
public bool IsAlive { get; set; }
}
// 要序列化的物件
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
// 把物件轉成 JSON 字串
string json = JsonSerializer.Serialize(player);
// 寫入檔案 — 安全!
File.WriteAllText("player.json", json);
// 從檔案還原物件:
string loadedJson = File.ReadAllText("player.json");
Player loadedPlayer = JsonSerializer.Deserialize<Player>(loadedJson)!;
序列化成 XML(需要嚴格格式時)
類別必須有 public 的無參數建構子
using System.Xml.Serialization;
// 範例:
var player = new Player { Name = "Aragorn", Health = 100, IsAlive = true };
var serializer = new XmlSerializer(typeof(Player));
using (var fs = File.Create("player.xml"))
{
serializer.Serialize(fs, player);
}
視覺化比較「舊的 vs 新的」
| 任務 | 舊方法 (BinaryFormatter) | 現代方法 |
|---|---|---|
| 序列化到檔案 | |
|
| 從檔案反序列化 | |
|
| 安全性 | 易遭攻擊 (RCE) | 安全的解析器與嚴格的模型 |
| 跨平台性 | 否 | 是 (JSON/XML/Protobuf) |
| 速度 | 快,但危險 | 快速且安全 |
5. 使用 XmlSerializer 時常見的錯誤
錯誤 №1:private 屬性或欄位。XmlSerializer 只會序列化 public 屬性;甚至 protected 也會導致錯誤。
錯誤 №2:缺少預設建構子。需要 public 的無參數建構子,否則序列化/反序列化會失敗。
錯誤 №3:資料型別不匹配。XmlSerializer 對 Dictionary、interface 與 delegate 支援不佳 — 建議使用包裝物件或改用其他格式。
錯誤 №4:循環參考 (circular reference)。XmlSerializer 不支援 A → B → A 這種物件圖;對這類情況通常會選擇用 JSON 並搭配適當設定。
GO TO FULL VERSION