C# Unity中接口字典序列化加载报错问题求助
Unity BinaryFormatter加载报错:ISaveData接口未标记可序列化的原因与解决
问题场景
在Unity项目中基于BinaryFormatter开发二进制存档系统,保存操作无报错,但加载时抛出以下异常:
SerializationException: Type 'TheCompany.SaveSystem.ISaveData' in Assembly 'TheCompany.SaveSystem, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
存读核心代码:
public void Save() { GameSave gameSave = new GameSave(GetSaveData()); BinaryFormatter binaryFormatter = new BinaryFormatter(); FileStream file = File.Create(Application.persistentDataPath + "/gamesave.save"); binaryFormatter.Serialize(file, gameSave); file.Close(); } public GameSave Load() { BinaryFormatter binaryFormatter = new BinaryFormatter(); FileStream file = File.Open(Application.persistentDataPath + "/gamesave.save", FileMode.Open); GameSave gameSave = (GameSave)binaryFormatter.Deserialize(file); file.Close(); return gameSave; }
数据类与接口定义:
[Serializable] public class GameSave { private Dictionary<Type, HashSet<ISaveData>> _savedata; public Dictionary<Type, HashSet<ISaveData>> Savedata => _savedata; public GameSave(Dictionary<Type, HashSet<ISaveData>> savedata) { _savedata = savedata; } } public interface ISaveData : ISerializable { }
已确认所有ISaveData实现类都标记了[Serializable](否则保存阶段会直接报错),测试时_savedata仅包含单个键值对。但将GameSave改为单个ISaveData字段时功能完全正常:
[Serializable] public class GameSave { public ISaveData Savedata; }
更新:将所有ISaveData的具体实现从结构体改为类后,异常消失。
问题根源
- 值类型(结构体)与接口的序列化冲突:当ISaveData实现为结构体(值类型)时,存入
HashSet<ISaveData>会触发装箱操作。BinaryFormatter处理泛型集合时,会优先检查集合声明的类型(即ISaveData接口)是否可序列化,而非实际存储的结构体实例。由于接口未标记[Serializable],反序列化时直接抛出异常。 - 单个字段与集合的序列化逻辑差异:单个
ISaveData字段场景下,BinaryFormatter能直接追踪到实际实例的结构体类型(带[Serializable]标记),完成序列化/反序列化;但泛型集合的元数据检查逻辑会优先校验接口类型,导致报错。
解决方案
方案1:将ISaveData实现从结构体改为类(已验证有效)
类是引用类型,装箱后BinaryFormatter能正确保留具体实现类的序列化信息,反序列化集合时会直接识别到带[Serializable]标记的类,跳过对接口类型的校验,从根本上解决问题。
方案2:给ISaveData接口添加[Serializable]特性(可选)
虽然接口本身无法实例化,但添加该特性可以让序列化系统跳过对接口类型的可序列化检查,转而处理实际存储的实现类型:
[Serializable] public interface ISaveData : ISerializable { }
注意:该方案仅能解决报错问题,结构体作为值类型在泛型集合中仍可能存在序列化隐患,优先推荐方案1。
方案3:避免在集合中直接使用接口类型(进阶优化)
将集合类型改为具体实现类的集合,或使用List<object>并手动保留类型信息,但会增加代码复杂度,适合有特殊需求的场景。
内容的提问来源于stack exchange,提问作者dreadi
相关产品推荐
相关产品推荐

