这个问题的讨论 元组.

我开始思考有关列码,一组应该有。如果我们将接受类型作为一组?这并不复盖GetHashCode()方法,因此可能不会知道的散列编码的,它的"儿童"...因此,运行时间将呼吁的对象。GetHashCode(),这是不知道的真正对象的结构。

然后我们就可以让两个实例中的一些基准类型,这实际上是平等的,因为的超载GetHashCode()以及平等().并利用它们作为"儿童"组"欺骗"的字典。

但是它不工作!运行时间以某种方式的数字结构,我们的多元组,并呼吁重载GetHashCode我们上课!

它是如何工作的?什么是分析的对象。GetHashCode()?

它可以影响的性能在一些不好的情况下,当我们使用了一些复杂的钥匙?(可能,不可能的情况...但仍)

考虑这个代码作为一个例子:

namespace csharp_tricks
{
    class Program
    {
        class MyClass
        {
            int keyValue;
            int someInfo;

            public MyClass(int key, int info)
            {
                keyValue = key;
                someInfo = info;
            }

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

                return keyValue.Equals(other.keyValue);
            }

            public override int GetHashCode()
            {
                return keyValue.GetHashCode();
            }
        }

        static void Main(string[] args)
        {
            Dictionary<object, object> dict = new Dictionary<object, object>();

            dict.Add(new KeyValuePair<MyClass,object>(new MyClass(1, 1), 1), 1);

            //here we get the exception -- an item with the same key was already added
            //but how did it figure out the hash code?
            dict.Add(new KeyValuePair<MyClass,object>(new MyClass(1, 2), 1), 1); 

            return;
        }
    }
}

更新 我想我已经找到一个解释这一点如下文所述的在我的答案。取得的主要成果是:

  • 小心你的钥匙他们的散列代码:-)
  • 对于复杂的字典键你必须复盖Equals()和GetHashCode()正确。
有帮助吗?

解决方案 4

似乎我有一个线索。

我想型为一个参考的类型,但它不是,这是一个结构。所以它使用ValueType.GetHashCode()方法。MSDN为它说:"一个或多个领域的派生的类型用于计算回报价值"。

如果你将采取一个真正的基准类型作为一个"元组提供者"你会作弊的词典(或自己...).

using System.Collections.Generic;

namespace csharp_tricks
{
    class Program
    {
        class MyClass
        {
            int keyValue;
            int someInfo;

            public MyClass(int key, int info)
            {
                keyValue = key;
                someInfo = info;
            }

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

                return keyValue.Equals(other.keyValue);
            }

            public override int GetHashCode()
            {
                return keyValue.GetHashCode();
            }
        }

        class Pair<T, R>
        {
            public T First { get; set; }
            public R Second { get; set; }
        }

        static void Main(string[] args)
        {
            var dict = new Dictionary<Pair<int, MyClass>, object>();

            dict.Add(new Pair<int, MyClass>() { First = 1, Second = new MyClass(1, 2) }, 1);

            //this is a pair of the same values as previous! but... no exception this time...
            dict.Add(new Pair<int, MyClass>() { First = 1, Second = new MyClass(1, 3) }, 1);

            return;
        }
    }
}

其他提示

不复盖GetHashcode()以及平等()上易变的课程,仅复盖它在不可改变课程或结构,其他的如果你修改的对象用作关键的散列表不会正常了(你不能检索的数值相关联的关键之后的关键对象是修改)

还散列表不要使用hashcodes识别对象,他们使用的密钥对象themselfes作标识符,也不要求所有的钥匙,用于添加条目中的散列表的回报不同hashcodes,但是建议,他们这样做,则性能会受到影响很大。

这里是适当的散列和平等的实现四元组(包含4元组件内)。这个代码确保正确使用这个特定的多元组在HashSets和词典。

更多关于这个问题(包括源代码) 在这里,.

注意到 使用 未经检查的 关键词(以避免外溢)和投掷NullReferenceException if obj is null(如需要通过基础的方法)

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj))
        throw new NullReferenceException("obj is null");
    if (ReferenceEquals(this, obj)) return true;
    if (obj.GetType() != typeof (Quad<T1, T2, T3, T4>)) return false;
    return Equals((Quad<T1, T2, T3, T4>) obj);
}

public bool Equals(Quad<T1, T2, T3, T4> obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    return Equals(obj.Item1, Item1)
        && Equals(obj.Item2, Item2)
            && Equals(obj.Item3, Item3)
                && Equals(obj.Item4, Item4);
}

public override int GetHashCode()
{
    unchecked
    {
        int result = Item1.GetHashCode();
        result = (result*397) ^ Item2.GetHashCode();
        result = (result*397) ^ Item3.GetHashCode();
        result = (result*397) ^ Item4.GetHashCode();
        return result;
    }
}
public static bool operator ==(Quad<T1, T2, T3, T4> left, Quad<T1, T2, T3, T4> right)
{
    return Equals(left, right);
}


public static bool operator !=(Quad<T1, T2, T3, T4> left, Quad<T1, T2, T3, T4> right)
{
    return !Equals(left, right);
}

看看这个 通过布拉德布拉姆斯和也评论通过布莱恩Grunkemeyer一些更多的信息,关于如何对象。GetHashCode工作。此外,看一下第一个评论Ayande的博客 .我不知道如果当前版本的框架中仍然遵循这些规则,或者如果他们实际上已经改变了它像布拉德的暗示。

我没有这本书的参考了,我会找到它只是为了确认,但我认为默认的基本哈希只是散列在一起的所有成员的对象。它得到了对它们的访问的方式,因为CLR工作,所以它不是东西,你可以写作,以及他们了。

完全从记忆的东西我简要宣读以采取它为什么你会的。

编辑: 这本书 内部C# 从MS新闻。有锯片。提交人花了很多时间解释的事情是如何实现在CLR,如何语言翻译下集等。等。如果你能找到这本书它不是一个坏阅读。

编辑: 形式提供的链接,它看起来像

对象。GetHashCode()使用一个 内部领域的系统。目类产生的散列值。每 创建的对象是分配一个独特的对象的关键,储存作为一个整数,当它 被创建。这些钥匙从1开始,并增加每一次新的目的 任何类型的得到创造。

嗯我猜猜我需要写一些我自己的散列代码,如果我预期使用对象为哈希键。

所以也许它不会知道的散列编码的,它的"儿童"。

你的例子似乎证明,否则:-)的散列代码的关键 MyClass 和价值 1 是相同的两个 KeyValuePair's。在型的执行必须使用它 KeyValue 对于其自己的散列码

动起来,这本字典类希望的唯一的钥匙。它是使用哈希码提供每个关键图的事情了。记住,运行时不叫 Object.GetHashCode(), 但它呼吁GetHashCode()执行情况提供的的实例得到它。

考虑一个更复杂的情况:

public class HappyClass
{

    enum TheUnit
    {
        Points,
        Picas,
        Inches
    }

    class MyDistanceClass
    {
        int distance;
        TheUnit units;

        public MyDistanceClass(int theDistance, TheUnit unit)
        {
            distance = theDistance;

            units = unit;
        }
        public static int ConvertDistance(int oldDistance, TheUnit oldUnit, TheUnit newUnit)
        {
            // insert real unit conversion code here :-)
            return oldDistance * 100;
        }

        /// <summary>
        /// Figure out if we are equal distance, converting into the same units of measurement if we have to
        /// </summary>
        /// <param name="obj">the other guy</param>
        /// <returns>true if we are the same distance</returns>
        public override bool Equals(object obj)
        {
            MyDistanceClass other = obj as MyDistanceClass;
            if (other == null) return false;

            if (other.units != this.units)
            {
                int newDistance = MyDistanceClass.ConvertDistance(other.distance, other.units, this.units);
                return distance.Equals(newDistance);
            }
            else
            {
                return distance.Equals(other.distance);
            }


        }

        public override int GetHashCode()
        {
            // even if the distance is equal in spite of the different units, the objects are not
            return distance.GetHashCode() * units.GetHashCode();
        }
    }
    static void Main(string[] args)
    {

        // these are the same distance... 72 points = 1 inch
        MyDistanceClass distPoint = new MyDistanceClass(72, TheUnit.Points);
        MyDistanceClass distInch = new MyDistanceClass(1, TheUnit.Inch);

        Debug.Assert(distPoint.Equals(distInch), "these should be true!");
        Debug.Assert(distPoint.GetHashCode() != distInch.GetHashCode(), "But yet they are fundimentally different values");

        Dictionary<object, object> dict = new Dictionary<object, object>();

        dict.Add(new KeyValuePair<MyDistanceClass, object>(distPoint, 1), 1);

        //this should not barf
        dict.Add(new KeyValuePair<MyDistanceClass, object>(distInch, 1), 1);

        return;
    }

}

基本上...在这种情况下,我如你想的两个对象是相同的距离,返回"真正的"为平等,但尚未返回不同的散列码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top