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

C# 字典没有';使用新对象作为键时找不到值,c#,dictionary,hash,hashmap,C#,Dictionary,Hash,Hashmap,在Unity3D中,我试图构建一个由节点坐标键控的PathNodes字典。我已经重写了NodeCoordinate中的GetHashCode,它应该返回一个常量唯一值。如果我循环遍历字典的键,查找工作正常,但是如果我使用应该存在的PathNode的坐标创建一个新的NodeCoordinate,即使哈希代码相等,查找也会失败 using UnityEngine; using System.Collections; using System.Collections.Generic; public

在Unity3D中,我试图构建一个由节点坐标键控的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;
    }
}
使用UnityEngine;
使用系统集合;
使用System.Collections.Generic;
公共类PathNode:MonoBehavior{
公共静态字典路径节点;
公共路径节点左;
公共路径节点权;
公共路径节点转发;
公共路径节点向后;
私有节点协调;
无效唤醒()
{
if(PathNode.pathNodes==null)
{
PathNode.pathNodes=新字典();
}
节点坐标坐标=新节点坐标(transform.position.x,transform.position.z);
this.coord=coord;
PathNode.pathNodes.Add(coord,this);
节点坐标leftChord=新节点坐标(坐标x-1,坐标z);
节点坐标右弦=新节点坐标(坐标x+1,坐标z);
节点坐标forwardChord=新节点坐标(坐标x,坐标z+1);
节点坐标后弦=新节点坐标(坐标x,坐标z-1);
if(PathNode.pathNodes.ContainsKey(leftChord))
{
this.left=PathNode.pathNodes[leftChord];
this.left.right=这个;
}
if(PathNode.pathNodes.ContainsKey(rightChord))
{
this.right=PathNode.pathNodes[rightChord];
this.right.left=这个;
}
if(PathNode.pathNodes.ContainsKey(forwardChord))
{
this.forward=PathNode.pathNodes[forwardChord];
this.forward.backward=这个;
}
if(PathNode.pathNodes.ContainsKey(backwardChord))
{
this.backward=PathNode.pathNodes[backwardChord];
this.backward.forward=这个;
}
}
私有静态bool debug=true;
无效更新()
{
如果(调试)
{
foreach(PathNode.pathNodes.Keys中的节点坐标坐标)
{
Log(coord+“:”+PathNode.pathNodes[coord]+“:”+coord.GetHashCode());
}
foreach(PathNode.pathNodes.Values中的PathNode节点)
{
nodecoord leftChord=新的nodecoord(node.coord.x-1,node.coord.z);
路径节点左节点;
Log(“Left:+leftChord+”:“+PathNode.pathNodes.TryGetValue(leftChord,out leftNode)+”:“+leftChord.GetHashCode());
}
调试=错误;
}
}
}
公共类节点坐标
{
公共浮动x;
公共浮动z;
公共节点坐标(浮点x、浮点z)
{
这个.x=x;
这个。z=z;
}
公共布尔等于(节点坐标坐标坐标)
{
返回(this.x==coord.x&&this.z==coord.z);
}
公共覆盖int GetHashCode()
{
返回string.Format(“{0}x{1}”,this.x,this.z).GetHashCode();
}
公共重写字符串ToString()
{
返回“坐标:”+this.x+“x”+this.z;
}
}
这是我的小调试的输出:


如您所见,在循环键时,哈希代码为2137067561和1824497336的查找工作正常,但当我实例化一个新节点坐标并尝试查找它时,它具有相同的哈希代码,但查找失败。知道为什么会这样吗?谢谢。

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

将字典的声明更改为:

if (PathNode.pathNodes == null)
        {
            PathNode.pathNodes = new Dictionary<NodeCoordinate, PathNode>(new NodeCoordinateComparer());
        }
if(PathNode.pathNodes==null)
{
PathNode.pathNodes=新字典(新节点坐标比较器());
}
补充如下:

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();
    }


}
公共类节点坐标比较器:IEqualityComparer
{
公共布尔等于(节点坐标a、节点坐标b)
{
返回(a.x==b.x&&a.z==b.z);
}
公共int GetHashCode(节点坐标坐标坐标)
{
返回string.Format(“{0}x{1}”,coord.x,coord.z).GetHashCode();
}
}

问题在于您的
节点坐标
类没有以字典将使用的方式定义相等。您有这样一种方法:

public bool Equals(NodeCoordinate coord)
。。。但是您既不重写
IEquatable
,也不重写
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}";
}
公共密封类节点坐标:IEquatable
{
公共浮点X{get;}
公共浮点Z{get;}
公共节点坐标(浮点x、浮点z)
{
X=X;
Z=Z;
}
公共覆盖等于(对象其他)=>等于(其他作为节点坐标);
公共布尔等于(节点坐标坐标)=>
coord!=null&&this.X==coord.X&&this.Z==coord.Z;
公共覆盖int GetHashCode()
{
int hash=23;
散列=散列*31+X;
散列=散列*31+Z;
返回散列;
}
公共重写字符串ToString()=>$“坐标:{X}X{Z}”;
}

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

我的回答解释了“出于某种原因”。基本上,字典不知道如何使用你的
Equals
方法。你的Equals应该是:a.Equals(b)。哈希将进行比较。现在,equals正在比较数字,而散列正在比较字符串,这可能是导致失败的原因。