简体   繁体   中英

How to make Dictionary find object key by value

In my application i need to use custom object as a key for dictionary. The problem is compare by reference, like we all know when using value types the compare work comparing by values but in objects it's compare by reference so even if the objects are equal they sored in different places in memory heap so it returns false

to do it right i need to override Equals and GetHashCode methods (i think correct me if i'm wrong)

i override the Equals Method and it's working:

bool IsEqual = dictionaryList.Keys.First().Equals(compareKey); returns true.

what i didn't know is how to override the GetHashCode method (and if i need) to my case.

Exception that i get :

The given key was not present in the dictionary. -

The given key was not present in the dictionary.

How can i solve that issue or maybe i doing it completely in wrong way...

Thank's

using System;
using System.IO;
using System.Threading;
using System.Linq;
using System.Collections.Generic;

public sealed class Program
{
    public class Options
    {
        public string x { get; set; }
        public string y { get; set; }
    }

    public class Data
    {
        public string myData { get; set; }
    }

    public class KeyClass
    {
        public int KeyNumber { get; set; }
        public List<Options> Options { get; set; }

        public override bool Equals(object obj)
        {
            KeyClass keyClass = obj as KeyClass;

            bool IsKeyNumberEqual = (KeyNumber == keyClass.KeyNumber);
            bool IsOptionsEqual = true;

            if (!(Options.Count == keyClass.Options.Count) || !IsKeyNumberEqual)
            {
                IsOptionsEqual = false;
            }
            else
            {
                for (int i = 0; i < Options.Count; i++)
                {
                    if (!(Options[i].x == keyClass.Options[i].x) ||
                        !(Options[i].y == keyClass.Options[i].y))
                    {
                        IsOptionsEqual = false;
                        break;
                    }
                }
            }

            return (IsKeyNumberEqual && IsOptionsEqual);
        }
    }

    public static void Main()
    {
        try
        {
            List<Options> optionsList = new List<Options>();
            optionsList.Add(new Options() { x = "x1", y = "y1" });
            optionsList.Add(new Options() { x = "x2", y = "y2" });

            Data someData = new Data() { myData = "someData" };
            Data getData = new Data();

            KeyClass dictionaryKey = new KeyClass() { KeyNumber = 1, Options = optionsList };
            KeyClass compareKey = new KeyClass() { KeyNumber = 1, Options = optionsList };

            Dictionary<KeyClass, Data> dictionaryList = new Dictionary<KeyClass, Data>();

            dictionaryList.Add(dictionaryKey, someData);

            bool IsEqual = dictionaryList.Keys.First().Equals(compareKey);

            getData = dictionaryList[compareKey];
        }
        catch (Exception ex)
        {
            string exMessage = ex.Message;
        }


    }
}

to do it right i need to override Equals and GetHashCode methods (i think correct me if i'm wrong)

You're correct. .NET requires that two objects that compare as equal have the same hash code. This is not limited to dictionaries.

The trivial implementation is to make every object return the same hash code. But although two different objects are allowed to have the same hash code, you should keep this to a minimum. When you have a lot of hash collisions, performance of dictionaries and other containers will be worse.

A slightly better implementation would be to return KeyNumber (or KeyNumber.GetHashCode() ). This can be a good enough implementation if you almost never have identical key numbers, if identical key numbers is a very strong indication that the options will be identical as well.

The best implementation would be to combine the hash codes of KeyNumber and all your Options values, as in Matthew Watson's answer.

You need to write a GetHashCode() that includes everything that contributes to the Equals() method.

For example:

public override int GetHashCode()
{
    unchecked
    {
        int hash = KeyNumber * 397;

        foreach (var opt in Options)
        {
            hash = hash*23 + opt.x.GetHashCode();
            hash = hash*23 + opt.y.GetHashCode();
        }

        return hash;
    }
}

If you implement GetHashCode() for your Options class, for example:

public class Options
{
    public readonly string x;
    public readonly string y;

    public override int GetHashCode()
    {
        return x.GetHashCode() ^ y.GetHashCode();
    }
}

Then you can write GetHashCode() more simply:

public override int GetHashCode()
{
    unchecked
    {
        int hash = KeyNumber * 397;

        foreach (var opt in Options)
            hash = hash*23 + opt.GetHashCode();

        return hash;
    }
}

One important thing I forgot to mention earlier:

It is MOST IMPORTANT that none of your fields that contribute to equality or hash code are changed after the object has been put into the dictionary.

If you change any of them after adding the object to the dictionary, it's likely that you will no longer be able to retrieve the object from the dictionary.

The best way to ensure this is to use only immutable fields for equality and hash code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM