字典没有';使用新对象作为关键字时找不到值

本文关键字:对象 关键字 找不到 新对象 字典 | 更新日期: 2023-09-27 17:58:20

在Unity 3D中,我正试图构建一个由NodeCoordinates键控的PathNodes字典。我已经在NodeCoordinate中重写了GetHashCode,它应该返回一个常量唯一值。如果我循环遍历字典的键,查找工作正常,但如果我用应该存在的PathNode的坐标创建一个新的NodeCoordinate,即使哈希码相等,查找也会失败。

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PathNode : MonoBehaviour {
    public static Dictionary<NodeCoordinate, PathNode> pathNodes;
    public PathNode left;
    public PathNode right;
    public PathNode forward;
    public PathNode backward;
    private NodeCoordinate coord;
    void Awake()
    {
        if (PathNode.pathNodes == null)
        {
            PathNode.pathNodes = new Dictionary<NodeCoordinate, PathNode>();
        }
        NodeCoordinate coord = new NodeCoordinate(transform.position.x, transform.position.z);
        this.coord = coord;
        PathNode.pathNodes.Add(coord, this);
        NodeCoordinate leftChord = new NodeCoordinate(coord.x - 1, coord.z);
        NodeCoordinate rightChord = new NodeCoordinate(coord.x + 1, coord.z);
        NodeCoordinate forwardChord = new NodeCoordinate(coord.x, coord.z + 1);
        NodeCoordinate backwardChord = new NodeCoordinate(coord.x, coord.z - 1);
        if (PathNode.pathNodes.ContainsKey(leftChord))
        {
            this.left = PathNode.pathNodes[leftChord];
            this.left.right = this;
        }
        if (PathNode.pathNodes.ContainsKey(rightChord))
        {
            this.right = PathNode.pathNodes[rightChord];
            this.right.left = this;
        }
        if (PathNode.pathNodes.ContainsKey(forwardChord))
        {
            this.forward = PathNode.pathNodes[forwardChord];
            this.forward.backward = this;
        }
        if (PathNode.pathNodes.ContainsKey(backwardChord))
        {
            this.backward = PathNode.pathNodes[backwardChord];
            this.backward.forward = this;
        }
    }
    private static bool debug = true;
    void Update()
    {
        if (debug)
        {
            foreach (NodeCoordinate coord in PathNode.pathNodes.Keys)
            {
                Debug.Log(coord + " : " + PathNode.pathNodes[coord] + " : " + coord.GetHashCode());
            }
            foreach (PathNode node in PathNode.pathNodes.Values)
            {
                NodeCoordinate leftChord = new NodeCoordinate(node.coord.x - 1, node.coord.z);
                PathNode leftNode;
                Debug.Log("Left: " + leftChord + " : " + PathNode.pathNodes.TryGetValue(leftChord, out leftNode) + " : " + leftChord.GetHashCode());
            }
            debug = false;
        }
    }
}
public class NodeCoordinate
{
    public float x;
    public float z;
    public NodeCoordinate(float x, float z)
    {
        this.x = x;
        this.z = z;
    }
    public bool Equals(NodeCoordinate coord)
    {
        return (this.x == coord.x && this.z == coord.z);
    }
    public override int GetHashCode()
    {
        return string.Format("{0}x{1}", this.x, this.z).GetHashCode();
    }
    public override string ToString()
    {
        return "Coordinate: " + this.x + " x " + this.z;
    }
}

这是我的小调试的输出:调试日志

正如您所看到的,当循环键时,使用哈希代码2137067561和1824497336的查找可以工作,但当我实例化一个新的NodeCoordinate并尝试查找它时,它具有相同的哈希代码,但查找失败。知道为什么会发生这种事吗?谢谢

字典没有';使用新对象作为关键字时找不到值

问题是NodeCoordinate类没有以字典使用的方式定义相等。你有一个这样的方法:

public bool Equals(NodeCoordinate coord)

但既不覆盖IEquatable<NodeCoordinate>,也不覆盖Equals(object)

就我个人而言,我建议两者兼而有之,并实现一个不需要字符串格式的更简单的哈希代码:

public sealed class NodeCoordinate : IEquatable<NodeCoordinate>
{
    public float X { get; }
    public float Z { get; }
    public NodeCoordinate(float x, float z)
    {
        X = x;
        Z = z;
    }
    public override Equals(object other) => Equals(other as NodeCoordinate);
    public bool Equals(NodeCoordinate coord) =>
        coord != null && this.X == coord.X && this.Z == coord.Z;
    public override int GetHashCode()
    {
        int hash = 23;
        hash = hash * 31 + X;
        hash = hash * 31 + Z;
        return hash;
    }
    public override string ToString() => $"Coodinate: {X} x {Z}";
}

(请注意,我还使其不可变,并使用属性而不是公共字段。为了简单起见,我使用了C#6语法,但如果必要的话,转换为C#5并不困难。)

因此,出于某种原因,当我添加具有完全相同逻辑的IEqualityComparer时,它可以工作。

将字典的声明更改为:

if (PathNode.pathNodes == null)
        {
            PathNode.pathNodes = new Dictionary<NodeCoordinate, PathNode>(new NodeCoordinateComparer());
        }

添加了以下内容:

public class NodeCoordinateComparer : IEqualityComparer<NodeCoordinate>
{
    public bool Equals(NodeCoordinate a, NodeCoordinate b)
    {
        return (a.x == b.x && a.z == b.z);
    }
    public int GetHashCode(NodeCoordinate coord)
    {
        return string.Format("{0}x{1}", coord.x, coord.z).GetHashCode();
    }

}