You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的具体实现从结构体改为类后,异常消失。


问题根源

  1. 值类型(结构体)与接口的序列化冲突:当ISaveData实现为结构体(值类型)时,存入HashSet<ISaveData>会触发装箱操作。BinaryFormatter处理泛型集合时,会优先检查集合声明的类型(即ISaveData接口)是否可序列化,而非实际存储的结构体实例。由于接口未标记[Serializable],反序列化时直接抛出异常。
  2. 单个字段与集合的序列化逻辑差异:单个ISaveData字段场景下,BinaryFormatter能直接追踪到实际实例的结构体类型(带[Serializable]标记),完成序列化/反序列化;但泛型集合的元数据检查逻辑会优先校验接口类型,导致报错。

解决方案

方案1:将ISaveData实现从结构体改为类(已验证有效)

类是引用类型,装箱后BinaryFormatter能正确保留具体实现类的序列化信息,反序列化集合时会直接识别到带[Serializable]标记的类,跳过对接口类型的校验,从根本上解决问题。

方案2:给ISaveData接口添加[Serializable]特性(可选)

虽然接口本身无法实例化,但添加该特性可以让序列化系统跳过对接口类型的可序列化检查,转而处理实际存储的实现类型:

[Serializable]
public interface ISaveData : ISerializable
{

}

注意:该方案仅能解决报错问题,结构体作为值类型在泛型集合中仍可能存在序列化隐患,优先推荐方案1。

方案3:避免在集合中直接使用接口类型(进阶优化)

将集合类型改为具体实现类的集合,或使用List<object>并手动保留类型信息,但会增加代码复杂度,适合有特殊需求的场景。

内容的提问来源于stack exchange,提问作者dreadi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:52:44