序列化保存实现思路
目前见过的序列化实现方法主要有以下两种
- 使用BinaryFormatter实现序列化和反序列化
- 使用Json实现序列化和反序列化
无论哪种反序列化,最重要的一点是只保存关键数据。对于中间信息,尤其是UI控件等。一律不要去保存。正确的思路是,我们在保存关键信息关闭程序后,下次程序打开加载信息,呼出UI界面时,让UI去从我们加载的内容中重新获取显示的内容。
对于第一种方法,由于目前存在安全隐患。现在已经微软官方已经不太推荐了。但对于.net9.0以下的版本仍然可以使用。参考:BinaryFormatter 迁移指南,并且这个方法在使用的时候比较麻烦,对于程序集的引用是强类型的引用。反序列化时如果程序集引入有问题,就会报错。
BinaryFormatter
使用BinaryFormatter进行序列化,需要配合文件流进行。通过BinaryFormatter将数据写入到指定的文件流中。

此外,对于需要序列化的类需要使用特性标明(Serializable),此外还可以通过特性指定某些字段不被序列化(NonSerialized)。

由于此方法只能序列化保存类中的字段信息,因此,对于无法序列化的内容,需要使用[OnDeserializing]或[OnDeserialized]在反序列化时重新赋值。
序列化方法
using (FileStream fs = new FileStream(tempSln, FileMode.Create))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
fs.Seek(0, SeekOrigin.Begin);
binaryFormatter.Serialize(fs, SysProcessSln.g_VarList);
}
对于反序列化,其实是一样的。就是从对应的文件流中把数据还原成对应的类。

反序列化
//反序列化 数据
using (FileStream fs = new FileStream(filepath, FileMode.Open))
{
fs.Seek(0, SeekOrigin.Begin);
BinaryFormatter binaryFormatter = new BinaryFormatter();
//变量
SysProcessSln.g_VarList = (List<DataVar>)binaryFormatter.Deserialize(fs);
}
但使用BinaryFormatter反序列化问题是有的,它在反序列化过程中会严格匹配当前类型以及其所在的程序集(版本号等)。如果匹配不成功就会报异常。 这个问题通常会出现在插件式开发中,程序反序列化不同的插件类时。
对于这个问题提供下面两个解决思路。
- 在反序列化使用的IFormatter 对象加入Binder 属性,使其获取要反序列化的对象所在的程序集。
- 项目在.NetFrameWork4.8环境下开发插件式开发时使用到了BinaryFormatter进行序列化和反序列化,不同插件的内容时,程序出现异常。解决方法:在主项目的APP.CONFIG文件中,将插件所在的目录绑定到了当前主项目的程序集下,从而解决了反序列化时的报错问题。

配置内容
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<publisherPolicy apply="yes"/>
<probing privatePath="Plugins"/>
</assemblyBinding>
</runtime>
runtime:用于定义应用程序运行时的行为
assemblyBinding:程序集绑定
publisherPolicy apply="yes":如果dll有最新版本,自动加载最新版本
privatePath="Plugins":表示运行时会在 基目录\Plugins\ 下查找未能在基目录中找到的程序集。
2025.9.20补充
使用BinaryFormatter进行序列化和反序列化时还有一个细节问题需要注意,因为前面的序列化方法是使用文件流保存BinaryFormatter得到的2进制数据。因此一定要让序列化和反序列化变量的顺序一致才行 否则可能出现赋值错误的问题。
Json的反序列化
这个推荐使用NewtonSoft.Json第三方库去完成对应的序列化和反序列。思路上大体一次,此处仅列举一列我遇到的问题以及解决方法。
序列化时的循环引用问题
接口反序列化为指定类
如果一个接口,被多个类实现了。但我们在序列化时又传入了该接口引用的实例,在反序列化过程中,由于我们需要实例化一个对应属性类型的实例,但接口又不允许实例化,就会产生报错。

使用类型名称标记
如果你需要在反序列化时根据接口类型还原具体实现类型,Newtonsoft.Json 提供了一种策略,可以在序列化过程中保存类型信息。这是通过在序列化时自动保存类型名称来实现的,使用 TypeNameHandling 配置。
解决方案
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public interface IAnimal
{
string Name { get; set; }
}
public class Dog : IAnimal
{
public string Name { get; set; }
public string Breed { get; set; }
}
var animal = new Dog { Name = "Buddy", Breed = "Labrador" };
// 配置序列化时保存类型信息
string json = JsonConvert.SerializeObject(animal, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All // 这里使用 TypeNameHandling 来保存类型信息
});
Console.WriteLine(json);
// 反序列化时可以正确恢复类型
IAnimal deserializedAnimal = JsonConvert.DeserializeObject<IAnimal>(json, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
Console.WriteLine(deserializedAnimal.GetType()); // 输出: Dog
反序列化后,列表元素重复添加的问题
当使用NewtonSoft.Json反序列化文件加载对应类属性时,发现类中的元素重复添加。
预期结果如下:
{"Property":[1,2,3],"MemberVariable":[4,5,6],"Array":[7,8,9]}。
但实际上结果是:
{"Property":[1,2,3,1,2,3],"MemberVariable":[4,5,6,4,5,6],"Array":[7,8,9]}
Newtonsoft.Json在反序列化时,会先调用对象的构造函数,然后再根据JSON字符串中的键值对来设置对象的属性或字段。如果对象的属性或字段已经在构造函数中被初始化了,那么反序列化时就会把JSON字符串中的值追加到原来的值上,而不是替换掉它们。
这种行为只会发生在列表类型的属性或字段上,因为列表类型有一个Add方法,可以把新的值添加到列表中。 而数组类型没有这个方法,所以反序列化时会直接替换掉原来的值。
- 一种方法是在对象的构造函数中不要初始化属性或字段,而是在属性的get方法中检查是否为空,并在需要时进行初始化。这样就可以避免在反序列化时调用构造函数导致的重复。
点击查看代码
public class Product
{
private List<int> _property;
public List<int> Property
{
get
{
if (_property == null)
{
_property = new List<int>() { 1, 2, 3 };
}
return _property;
}
set
{
_property = value;
}
}
// 省略其他属性和构造函数
}
2.另一种方法是使用[JsonIgnore]特性来忽略不需要序列化或反序列化的属性或字段。这样就可以避免在反序列化时设置对象的属性或字段导致的重复。
点击查看代码
public class Product
{
[JsonIgnore]
public List<int> Property { get; set; }
// 省略其他属性和构造函数
}
- 还有一种方法是使用[ObjectCreationHandling]设置来控制反序列化时对象的创建方式。
这个设置有三个选项:Auto,Reuse和Replace。
Auto是默认的选项,它会根据对象的类型来决定是重用还是替换。
Reuse表示会重用已经存在的对象,并把JSON字符串中的值追加到对象上。
Replace表示会创建一个新的对象,并把JSON字符串中的值赋给对象。
这样就可以避免在反序列化时追加列表项导致的重复。例如:
点击查看代码
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(json, new JsonSerializerSettings
{
ObjectCreationHandling = ObjectCreationHandling.Replace
});

浙公网安备 33010602011771号