如何使Dictionary按值

时间:2016-10-31 14:09:43

标签: c# collections override

在我的应用程序中,我需要使用自定义对象作为字典的键。 问题是通过参考比较, 就像我们都知道在使用值类型时,比较工作按值进行比较,但是在对象中它通过引用进行比较,因此即使对象相等,它们也会在内存堆中的不同位置进行存储,因此它返回false

要做得对,我需要覆盖Equals和GetHashCode方法(如果我错了,我认为纠正我)

我覆盖了Equals Method并且它正在工作:

bool IsEqual = dictionaryList.Keys.First().Equals(compareKey);返回true。

我不知道如何覆盖我的案例中的GetHashCode方法(如果我需要)。

我得到的例外:

The given key was not present in the dictionary. -

  

字典中没有给定的密钥。

我如何解决这个问题,或者我可能以错误的方式完成这个问题......

感谢' 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;
        }


    }
}

2 个答案:

答案 0 :(得分:4)

  

要做得对,我需要覆盖Equals和GetHashCode方法(如果我错了,我认为纠正我)

你是对的。 .NET要求两个比较相等的对象具有相同的哈希码。这不仅限于字典。

简单的实现是使每个对象返回相同的哈希码。但是,虽然允许两个不同的对象具有相同的哈希码,但您应该将其保持在最低限度。当你有很多哈希冲突时,字典和其他容器的性能会更差。

稍微好一点的实现是返回KeyNumber(或KeyNumber.GetHashCode())。如果您几乎从不拥有相同的密钥编号,如果相同的密钥编号非常强烈地表明选项也是相同的,那么这可以是一个足够好的实现。

最好的实现方法是将KeyNumber的哈希码与所有Options值结合起来,如Matthew Watson所说。

答案 1 :(得分:2)

您需要撰写GetHashCode(),其中包含有助于Equals()方法的所有内容。

例如:

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

如果为Options类实现GetHashCode(),例如:

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

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

然后你可以更简单地写一下GetHashCode()

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

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

        return hash;
    }
}

我之前忘记提到的一件重要事情:

最重要的是,在将对象放入字典后,没有任何有助于相等或哈希代码的字段会被更改。

如果在将对象添加到字典后更改其中任何一个,则很可能您将无法再从字典中检索该对象。

确保这一点的最佳方法是仅使用不可变字段进行相等和哈希码。