【C#】字典Dictionary踩坑日记&注意事项

得益于开发人员在背后做了很多看不见的工作,C#的字典简单又好用。但最近还是在字典上踩坑了,排查了几个小时最后才发现是字典的问题。类似的问题以前也踩过一次坑,不过说到底还是值类型/引用类型的问题。

不要修改值类型的键和值

字典的键是值类型时,不要去修改键的内容,否则会报错:字典中没有该键。

字典采用了哈希的原理,通过键的哈希去索引值。当修改键的内容时,键的哈希也随之改变。

public struct Test_Struct
{
    public int i;
    public Test_Struct(int _i)
    {
        i = _i;
    }
    public void Change(int _i)
    {
        i = _i;
    }
}
private void Start()
{
    Test_Struct dic1Key = new Test_Struct(1);
    //创建一个以值类型作为键的字典。注意,初始化后字典中的键是dic1Key的拷贝
    Dictionary<Test_Struct, int> dic1 = new() { { dic1Key, 2 } };
    Debug.Log($"dic1 Value:{dic1[dic1Key]}");//输出:2
    //尝试修改字典中键的值会弹出编辑器错误:无法修改返回值,因为它不是变量。我们访问到的是字典中的键的拷贝,修改它是无意义的,因此编辑器会直接禁止此行为。
    //dic1.Keys.First().i = 2;
    //如果绕过此错误,尝试通过函数去修改会怎样?
    dic1.Keys.First().Change(2);
    Debug.Log($"dic1 Key:{dic1.Keys.First().i}");//输出:1,也就是说我们之前修改的只是一个拷贝,这个行为是毫无意义的
    Debug.Log($"dic1 Value:{dic1[dic1Key]}");//输出2
    //修改手上持有的键的值
    dic1Key.i = 2;
    //Debug.Log($"dic1 Value:{dic1[dic1Key]}");//错误:KeyNotFoundException: The given key 'Test_Struct' was not present in the dictionary.
    Debug.Log($"dic1 Key:{dic1.Keys.First().i}");//输出:1,修改手上持有的键并不会影响字典中的键
    Debug.Log($"dic1 Value:{dic1[new Test_Struct(1)]}");//输出:2,即使new了一个新的值类型,由于内容相同,它们的哈希值也是相同的
    Debug.Log($"Hash:{dic1.Keys.First().GetHashCode() == new Test_Struct(1).GetHashCode()}");//输出:True
}

如果必须修改字典的值类型键,可以先删除旧的键值对,再添加一个新的键值对。

同样,字典的值是值类型时,不要去修改值的内容。原理相同,但是更具有迷惑性。

Dictionary<int, Test_Struct> dic2 = new() { { 1, new Test_Struct(2) } };
Debug.Log(dic2[1].i);//输出:2
//编辑器错误:无法修改返回值,因为它不是变量。与键一样,访问到的是拷贝。
//dic2[1].i = 3;
//使用函数绕过该错误,同样毫无意义。不同的是,这样写能够通过编译。
dic2[1].Change(3);
Debug.Log(dic2[1].i);//输出:2

被这个问题卡了几个小时,最后发现原理其实就这么简单。究其原因,是对键的修改更加敏感,而对值的修改不那么敏感。解决方法就是用修改过的值直接覆盖原来的值。

P.S.

实测,即使键的类型为引用类型,修改键的内容在某些情况下也会出错,报错仍是找不到键,原因不明。目前尚未能成功复现,但这个错误确实曾在项目中困扰了我数日,最后不得已只能将所有引用类型的键改为string,然后给原先作为键的每个实例对象分配个唯一的GUID作为键值,问题才得以解决。

因此,个人推荐优先选择值类型作为键。或着当遇到和我一样错误的时候,可以试试把键改为值类型看看能不能解决问题。

© 版权声明
THE END
喜欢就点个赞吧
点赞0 分享
评论 共1条

请登录后发表评论

    • Acoloco的头像-水波萌Acoloco等级-LV4-水波萌作者0