【问题标题】:Serializing a dictionary in C#在 C# 中序列化字典
【发布时间】:2015-04-04 03:13:35
【问题描述】:

我有一个名为 serializableVector2 的类:

[Serializable]
class serializableVector2
{
    public float x, y;
    public serializableVector2(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
}

我有一个名为 savedMapTile 的结构:

[Serializable]
struct savedMapTile
{
    public oreInstance ore;
    public int backgroundTileId;
    public int playerId;
    public tree tree;
}

我有一本使用这两个类的字典:

[SerializeField]
Dictionary<serializableVector2, savedMapTile> savedTiles;

我正在尝试加载这个字典修改它,然后使用序列化再次保存它。

我正在像这样反序列化字典:

FileStream f = File.Open(saveFileName, FileMode.Open);
BinaryFormatter b = new BinaryFormatter();
savedTiles = (Dictionary<serializableVector2, savedMapTile>)b.Deserialize(f);
f.Close();

我正在像这样序列化它:

FileStream f = File.Open(saveFileName, FileMode.Create);
BinaryFormatter b = new BinaryFormatter();
b.Serialize(f, savedTiles);
f.Close();

但是,当我尝试访问我知道应该存在的字典中的元素时,我收到以下错误:

System.Collections.Generic.KeyNotFoundException:给定的密钥不是 出现在字典中。

运行此代码时出现此错误:

id = (savedTiles[new serializableVector2(-19,13)].backgroundTileId);

我发现真正奇怪的是,我还能够打印出整个字典键及其值。这是我得到 Vector2 的值 -19 和 13 的地方。我像这样打印键和值:

for (int i = 0; i < 100; i++ )
{
    UnityEngine.Debug.Log(vv[i].x +" "+vv[i].y);
    UnityEngine.Debug.Log(x[i].backgroundTileId);
}

此时我真的很难过,我不知道发生了什么。我可以看到文件保存在 Windows 资源管理器中,我可以访问字典中的键和值,但我似乎无法正确使用它。同样重要的是要注意,当我在字典上使用 .Contains() 方法时,与尝试访问值的方式类似,它总是返回 false。

这是一个 Unity 5 项目,在 Windows 8.1 上运行的 Visual Studio 中使用 C#。

【问题讨论】:

  • 除其他外,您的FileStream 实例需要位于using 块中。

标签: c# serialization dictionary unity3d


【解决方案1】:

将您的serializableVector2class 更改为struct,您应该能够在字典中找到内容。如果我有这个错误,有人可能会纠正我,但据我所知,Dictionary 将调用键上的GetHashCode 并使用该代码将项目存储在字典中。如果您创建具有相同 x 和 y 坐标的类的两个实例并调用GetHashCode,您将看到两个实例产生不同的哈希码。如果您将其更改为struct,它们将产生相同的哈希码。我相信这就是导致您遇到“找不到密钥”问题的原因。在一些相关的注释中,构造函数将int 用于 x 和 y 然后将它们存储为浮点数似乎很奇怪。您可能需要考虑将构造函数更改为采用float

[Serializable]
struct serializableVector2
{
    public float x, y;
    public serializableVector2(float x, float y)
    {
        this.x = x;
        this.y = y;
    }
}

【讨论】:

  • 不要依赖浮点相等。 Dictionary 依赖于 GetHashCode 和 Equals,如果任何对应的浮点值甚至略有不同,两者都会给出不同的结果。
  • @Huacanacha - 谢谢。我忘记了 Equals 部分,这对于“包含”和碰撞处理都很有意义。
【解决方案2】:

你有两个问题:

  1. 您的字典键serializableVector2 是一个依赖于默认相等和散列方法的类。默认使用引用相等,这样只有指向同一个对象的变量才会相等并返回相同的哈希值。
  2. 如果不是这种情况,您仍将依赖浮点相等。除非您的序列化可以保证精确存储和检索浮点值,否则反序列化的 serializableVector2 可能不等于原始值。

建议的解决方案:

为您的 serializableVector2 类覆盖 GetHashCodeEquals。在执行比较和散列时,将浮点数舍入到预期值范围的 32 位浮点精度内。您可以依赖 6+ 位有效数字的精度(在同一范围内),所以如果您的世界是 += 1000 个单位,我相信您可以安全地四舍五入到小数点后 3 位。

GetHashCode 示例(未经测试):

public override int GetHashCode() {
    return Math.Round(x,3).GetHashCode() ^ Math.Round(y,3).GetHashCode();
} 

【讨论】:

    猜你喜欢
    • 2017-05-17
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-08
    相关资源
    最近更新 更多