【问题标题】:KeyNotFound Exception in Dictionary(of T)字典中的 KeyNotFound 异常(T)
【发布时间】:2011-01-27 10:19:15
【问题描述】:

我已经准备好用头撞墙了

我有一个名为 Map 的类,它有一个名为 tiles 的字典。

class Map
{
    public Dictionary<Location, Tile> tiles = new Dictionary<Location, Tile>();
    public Size mapSize;

    public Map(Size size)
    {
        this.mapSize = size;
    }
   //etc...

我暂时填了这本字典来测试一些东西..

public void FillTemp(Dictionary<int, Item> itemInfo)
    {
        Random r = new Random();
        for(int i =0; i < mapSize.Width; i++)
        {
            for(int j=0; j<mapSize.Height; j++)
            {
                Location temp = new Location(i, j, 0);

                int rint = r.Next(0, (itemInfo.Count - 1));

                Tile t = new Tile(new Item(rint, rint));

                tiles[temp] = t;
            }
        }

    }

在我的主程序代码中

Map m = new Map(10, 10);
m.FillTemp(iInfo);
Tile t = m.GetTile(new Location(2, 2, 0)); //The problem line

现在,如果我在我的代码中添加一个断点,我可以清楚地看到我的地图类的实例 (m) 通过上面的函数填充了对,但是当我尝试使用 GetTile 函数访问一个值时:

    public Tile GetTile(Location location)
    {
        if(this.tiles.ContainsKey(location))
        {
            return this.tiles[location]; 
        }
        else
        {
            return null;
        }
    }

它总是返回 null。同样,如果我在 Map 对象内部查看并找到 x=2,y=2,z=0 的 Location 键,我清楚地看到该值是 FillTemp 生成的 Tile..

为什么要这样做?到目前为止,我对这样的字典没有任何问题。我不知道为什么它返回null。再一次,在调试时,我可以清楚地看到 Map 实例包含它说它没有的 Location 键...... 非常令人沮丧。

有什么线索吗?需要更多信息吗?

非常感谢您的帮助:)

【问题讨论】:

  • 如果你写了一个简单的例子来说明问题会很有帮助。我试图为此设置一个测试,并且必须定义一些类。最后一个障碍是 Map 只定义了一个 Map(Size size) 构造函数,但代码调用了 new Map(10, 10)。只能看到一部分代码很难调试。

标签: c# dictionary equals keynotfoundexception


【解决方案1】:

您没有显示“位置”是什么,但如果它是一个类,这是正常行为:通过比较引用来测试对象是否相等。所以不同的 Location 实例总是不相等的,即使它们的内容相同。

最快的解决方法是对 Location 类使用 override Equals() 和 GetHashCode()。然后将它(重新)设计为不可变类(或者可能是不可变结构)是一个好主意。

【讨论】:

  • 啊哈,我完全明白了。我已经重写了 Equals,但我会考虑对 GetHashCode 和可能的 == 和 != 做同样的事情。另外,我会研究不变性.. 非常感谢
  • @C Patton:如果你已经重写了 Equals,你应该已经收到关于不重写 GetHashCode 的警告。始终检查警告:)
【解决方案2】:

亨克是正确的;当您在 .Net 中测试两个对象是否相等时,您实际上是在询问“引用 x 是否指向与引用 y 相同的对象”。

所以,默认情况下:

Location a = new Location(2, 2, 0);
Location b = new Location(2, 2, 0);
Location c = a;

bool notEqual = ( a == b );  // false
bool equal = ( a == c );     // true

要解决这个问题,您需要 override the equality methods 为您的 Location 对象比较值是否相等 - 例如,您的 Equals 方法的主体可能会以如下方式结束我们:

return (this.x == that.x && this.y == that.y && this.z == that.z);

【讨论】:

  • 是的,但一定要使用指南。必须一致地覆盖多个成员。
  • 您不会覆盖运算符,而是重载它们 - 而且实际上不需要重载 == 类型才能在其中工作用于引用相等但不同的对象的字典。您需要覆盖 Equals 和 GetHashCode,这不是一回事。完全可以覆盖 Equals/GetHashCode 而无需重载任何运算符。
  • 请注意,Microsoft 指南建议避免在可变引用类型上重载相等运算符。您可以将 Location 设为值类型。您还需要重载/覆盖 !=、Equals 和 GetHashCode,并可能实现 T 的 IEquatable。这就是我尽量避免走这条路的原因。
  • @Jon Skeet - 哎呀,太尴尬了。我现在已经修复了它(尽管在我 [可怜的] 辩护中,我链接到的文章也同样不正确)。 @Henk Holterman 和@TrueWill - 这些都是好点。您需要考虑重载的完整方法列表包含在我链接到的答案中,但 OP 还应考虑使类型不可变或值类型。
【解决方案3】:

谢谢大家!我真的很感激!

我将 Location 设置为值类型,现在它工作得很好。

很抱歉没有发布整个代码,但我觉得没有必要,并假设读者可以假设 location 是一个具有 x,y,z int 值的简单类。

因为你们所有人,我学到了很多新东西,我对这门(通常)美妙的语言的理解也因此而更加深刻:o)

保重,

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-10
    • 2015-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多