С# Общий словарь 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();
    }    
}

Спасибо всем, кто ответил на это.

Ответ 1

Вам нужно переопределить 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);
}

Ответ 2

Поскольку классы сравниваются по умолчанию с использованием сравнения ссылок.

Если вы сравниваете два объекта, которые вы делаете object.ReferenceEquals(obj1, obj2)

Если вы сравниваете две структуры, вы делаете сравнение значений (например, при сравнении двух ints).

Если вы хотите сравнить два объекта MyKey, вам необходимо реализовать собственный метод Equals и GetHashCode, и он будет автоматически использоваться в словаре.

Ответ 3

Struct - тип значения, а Class - ссылочный тип, поэтому, когда вы используете struct, все значения внутри него сравниваются, но при использовании класса вместо этого проверяется только ссылка на объект.

Вы можете изменить это поведение для определенных классов, переопределив метод Equals(). Вы также можете переопределить оператор ==, если хотите. См. Примеры Рекомендации по перегрузке Equals() и Operator == (Руководство по программированию на С#).

Edit:

ваш метод 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);
    }