字典键作为对象返回 KeyNotFoundError

Dictionary Key as object returning KeyNotFoundError

我正在尝试在 Unity 中实现 Dijkstras 寻路算法。我有一个字典 distances,其中包含所有可以行走的可能节点,这些节点是从 GridData.WalkableNodes 获得的。当尝试获取给定节点的已找到邻居的距离时,出于某种原因我无法从 distances 字典中获取距离值。它给了我一个 KeyNotFoundError。我做错了什么但使用 Node 对象作为键吗?在我的测试中,您可以使用对象作为键从字典中检索值,所以我认为这不是问题所在。

脚本文件可以在pastebin上看到 GridData.cs Dijkstra.cs

Dictionary<Node, int> distances = GridData.WalkableCells.ToDictionary(x => x, x => int.MaxValue);

foreach (Node neighbour in neighbours)
{

    if (!visited.Contains(neighbour.Position))
    {
        int dist = distances[currentCell] + 1;

        if (dist < distances[neighbour]) // Key not found happens here!
        {
            distances[neighbour] = dist;

            priorityQueue.Enqueue(neighbour, dist);
            visited.Add(neighbour.Position);

            neighbour.parentNode = currentCell;
            parents.Add(neighbour);
        }
    }
}

如果我使用 Vector3 而不是 Node 作为键,它似乎可以工作,但我不明白为什么在使用 Node 时它不起作用。

Dictionary<Vector3, int> distances = GridData.WalkableCells.ToDictionary(x => x.Position, x => int.MaxValue);

foreach (Node neighbour in neighbours)
{

    if (!visited.Contains(neighbour.Position))
    {
        int dist = distances[currentNode.Position] + 1;

        if (dist < distances[neighbour.Position]) 
        {
            distances[neighbour.Position] = dist;

            priorityQueue.Enqueue(neighbour, dist);
            visited.Add(neighbour.Position);

            neighbour.parentNode = currentCell;
            parents.Add(neighbour);
        }
    }
}

节点class

public class Node
{
    public Vector3 Position { get; set; }
    public CellType CellType { get; set; }
    public int Cost { get; set; }
    public Node parentNode {get; set;}

public Node(Vector3 position, CellType cellType, int cost)
{
    Position = position;
    CellType = cellType;
    Cost = cost;
}
}

您正在使用 new Node 来填充哈希图和列表。

因此,这些新创建的 Node 当然不是 与添加到您的字典中的实例 完全相同的实例!

它适用于 Vector3,因为它实现了 IEquatable<Vector3> and also GetHashCode

如果您没有明确覆盖 Equals,则默认情况下 reference equality is used (see object.Equals)


您应该简单地使用位置作为键,因为您已经知道它已经实现了。或者只是确保也实现它并使用位置作为哈希码提供者:

public class Node : IEquatable<Node>
{
    public Vector3 Position { get; set; }
    public CellType CellType { get; set; }
    public int Cost { get; set; }
    public Node parentNode {get; set;}

    public Node(Vector3 position, CellType cellType, int cost)
    {
        Position = position;
        CellType = cellType;
        Cost = cost;
    }

    public override int GetHashCode()
    {
        return Position.GetHashCode();
    }

    public bool Equals(Node other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }

        if (ReferenceEquals(this, other))
        {
            return true;
        }

        return GetHashCode() == other.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        if (ReferenceEquals(this, obj))
        {
            return true;
        }

        if (obj.GetType() != this.GetType())
        {
            return false;
        }

        return Equals((Node)obj);
    }
}

如果你想考虑其他属性,如 CelltypeCost 等,并且可以在同一位置有多个节点,你可以简单地扩展它,例如

public override int GetHashCode()
{
    return HashCode.Combine(Position, Cost, CellType);
}

根据您的需要。