BinaryFormatter 已废弃且不安全,应改用 System.Text.Json:需确保类型可序列化、避免 Type/委托等非法字段、路径跨平台、写入加锁并原子替换。

用 BinaryFormatter 保存对象?别用了,它已废弃且不安全
.NET Core 5+ 和 .NET 6+ 中,BinaryFormatter 已被标记为 Obsolete,不仅在 .NET 5+ 默认禁用反序列化,还会抛出 SerializationException 或运行时拒绝执行。它存在严重反序列化漏洞,微软明确建议停用。
常见错误现象:System.Runtime.Serialization.SerializationException: BinaryFormatter is disabled;或在 .NET Framework 项目中看似能跑,但上线后被安全扫描工具直接标红。
- 替代方案优先选
System.Text.Json(.NET Core 3.0+ 内置)或Newtonsoft.Json - 若必须保留二进制格式(如兼容旧协议),改用
System.Text.Json+MemoryStream+File.WriteAllBytes组合,而非BinaryFormatter.Serialize - 所有要序列化的类必须有无参构造函数(
System.Text.Json默认要求),且字段/属性需为 public 或标注[JsonPropertyName]/[JsonInclude]
用 System.Text.Json 保存对象到本地文件的最小可行写法
这是当前最轻量、安全、跨平台的默认选择。它不依赖第三方包,序列化结果是标准 JSON,人可读、调试方便,也便于后期转为网络传输格式。
使用场景:缓存用户设置、临时状态、小型配置对象(如 UserPrefs、AppCacheState),非高频写入(每秒不超过几次)。
- 确保类型可被 JSON 序列化:避免
DateTimeOffset在低版本 .NET 中的兼容问题,推荐统一用DateTime或显式配置JsonSerializerOptions - 写文件前建议先写入临时路径,再
File.Move替换,防止中断导致缓存损坏 - 示例(简化版):
var options = new JsonSerializerOptions { WriteIndented = true }; var json = JsonSerializer.Serialize(myObject, options); File.WriteAllText(@"C:\cache\state.json", json);
遇到 NotSupportedException: Serialization and deserialization of 'System.Type' instances are not supported 怎么办
这是 System.Text.Json 最常报的类型错误之一,本质是试图序列化含 Type、MethodInfo、委托、IntPtr 等运行时元数据的对象——它们无法被 JSON 表达。
典型场景:你缓存了一个包含 Func 字段的类,或用了 typeof(string) 作为属性值。
- 检查对象树:用
DebuggerDisplay或打印myObject.GetType().GetFields().Select(f => f.Name + ":" + f.FieldType)快速定位非法字段 - 解决方案不是“绕过”,而是重构:把
Type改成字符串(如typeName = typeof(MyClass).FullName),反序列化时再用Type.GetType(name) - 对不可序列化字段加
[JsonIgnore],或实现JsonConverter自定义处理逻辑(仅当真有必要)
缓存文件路径和并发写入怎么不出错
本地缓存不是“随便扔个文件就行”。多线程/多进程同时写同一文件会丢数据,路径硬编码会导致权限失败或跨平台失效。
容易踩的坑:在 Windows 上用 C:\temp\cache.bin,发布到 Linux 容器就直接 UnauthorizedAccessException;或 UI 线程和后台任务同时调用 Save() 导致 JSON 文件内容截断。
- 路径必须用
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)或Path.Combine(ApplicationData, "MyApp", "cache.json") - 写操作务必加锁:简单场景用
lock (_cacheLock);高并发考虑SemaphoreSlim或改用内存映射文件(MemoryMappedFile) - 不要用
File.WriteAllText直接覆盖:先File.WriteAllText(tempPath),再File.Replace(tempPath, finalPath, backupPath),保证原子性
真正难的从来不是“怎么序列化”,而是“怎么让缓存既快又稳还不崩”。路径、并发、类型边界、异常恢复——这些细节堆起来,才是落地时卡住半天的地方。










