问题 C#通用字典TryGetValue找不到键


我有这个简单的例子:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<MyKey, string> data = new Dictionary<MyKey, string>();
            data.Add(new MyKey("1", "A"), "value 1A");
            data.Add(new MyKey("2", "A"), "value 2A");
            data.Add(new MyKey("1", "Z"), "value 1Z");
            data.Add(new MyKey("3", "A"), "value 3A");

            string myValue;
            if (data.TryGetValue(new MyKey("1", "A"), out myValue))
                Console.WriteLine("I have found it: {0}", myValue );

        }
    }

    public struct MyKey
    {
        private string row;
        private string col;

        public string Row { get { return row; } set { row = value; } }
        public string Column { get { return col; } set { col = value; } }

        public MyKey(string r, string c)
        {
            row = r;
            col = c;
        }
    }
}

这工作正常。但是,如果我以这种方式通过MyKey类更改MyKey结构:

public class MyKey

然后方法 TryGetValue 尽管关键在那里,却找不到任何钥匙。

我确信我错过了一些明显的东西,但我不知道是什么。

任何想法 ?

谢谢


**解决方案**

(请参阅已接受的解决方案以获得更好的GetHashCode解析)

我已经像这样重新定义了MyKey类,现在一切正常:

public class MyKey
{
    private string row;
    private string col;

    public string Row { get { return row; } set { row = value; } }
    public string Column { get { return col; } set { col = value; } }

    public MyKey(string r, string c)
    {
        row = r;
        col = c;
    }

    public override bool Equals(object obj)
    {
        if (obj == null || !(obj is MyKey)) return false;

        return ((MyKey)obj).Row == this.Row && ((MyKey)obj).Column == this.Column;
    }

    public override int GetHashCode()
    {            
        return (this.Row + this.Column).GetHashCode();
    }    
}

感谢所有人的回答。


9865
2018-04-02 12:15


起源

无论如何,使用可变类作为关键是一个坏主意。 - Marc
使用任何可变的密钥是一个坏主意。 @Ferpt:保持原样 struct 但放弃了 set 来自物业的零件。 - Henk Holterman
@HenkHolterman是的,但至少有一个结构,他们并没有真正改变存储的密钥;) - Marc
您应该将解决方案作为答案发布(因此可能会因为哈希代码功能不佳而被低估) - RJFalconer
@RJFalconer你是对的。问题的解决方案是一个糟糕的实现。但这个例子只是一个例子。而解决方案只是一个'概念'解决方案。我已经添加了一条注释来指出未来的访问者它只是一个线索,而不是一个好的实现。 - ferpega


答案:


你需要覆盖 Equals() 和 GetHashCode() 在课堂里 MyKey

也许是这样的:

GetHashCode的()

public override int GetHashCode()
{
   return GetHashCodeInternal(Row.GetHashCode(),Column.GetHashCode());
}
//this function should be move so you can reuse it
private static int GetHashCodeInternal(int key1, int key2)
{
    unchecked
    {
        //Seed
        var num = 0x7e53a269;

        //Key 1
        num = (-1521134295 * num) + key1;
        num += (num << 10);
        num ^= (num >> 6);

        //Key 2
        num = ((-1521134295 * num) + key2);
        num += (num << 10);
        num ^= (num >> 6);

        return num;
    }
}

等于

public override bool Equals(object obj)
{
    if (obj == null)
        return false;
    MyKey p = obj as MyKey;
    if (p == null)
        return false;

    // Return true if the fields match:
    return (Row == p.Row) && (Column == p.Column);
}

6
2018-04-02 12:18



我想你的 GetHashCodeInternal 是复杂的,我已经简化了 GetHasCode 在问题解决方案中。我的简化版可以有任何错误吗? - ferpega
@FerPt, 5 + 1 具有相同的哈希值 1 + 5 在你的实施中。 (或等于相同数字的列/行的任意组合) - Marc
就像@Marc所说,你的哈希码可能是相同的。这就是我为你提供更复杂的原因 GetHashCode()。 - Arion
感谢@ Marc / Arion的解释。 - ferpega


因为默认情况下使用引用比较来比较类。

如果你比较两个对象你正在做一个object.ReferenceEquals(obj1,obj2)

如果比较两个结构,则表示您正在进行值比较(例如,比较两个整数时)。

如果要比较两个MyKey对象,则需要实现自己的对象 Equals 和 GetHashCode 方法,它将由字典自动使用。


4
2018-04-02 12:17



好的,我如何按其值搜索类? - ferpega
覆盖Equals方法。 - Ignacio Soler Garcia
您还需要覆盖GetHashCode,否则字典将找不到您的密钥。 - Falanwe
在HashTable / Dictionary中,应调整其GetHashCode()。但要做好工作,请同时覆盖:Equals和GetHashCode。否则,您将在List中有不同的行为 - Kelon


Struct是值类型而Class是引用类型,因此当您使用struct时,它内部的所有值都会被比较,但是当您使用class时,只会检查对象引用。

您可以通过覆盖更改某些类的行为 Equals() 方法。你也可以覆盖 == 运营商,如果你想。查看样品 重载等于()和运算符的指南==(C#编程指南)

编辑:

你的 Equals() 方法应该是这样的:

public override bool Equals(System.Object obj)
    {
        MyKey p = obj as MyKey;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (row == p.row) && (col == p.col);
    }

3
2018-04-02 12:18