题
这个问题的讨论 元组.
我开始思考有关列码,一组应该有。如果我们将接受类型作为一组?这并不复盖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);
}
我没有这本书的参考了,我会找到它只是为了确认,但我认为默认的基本哈希只是散列在一起的所有成员的对象。它得到了对它们的访问的方式,因为CLR工作,所以它不是东西,你可以写作,以及他们了。
完全从记忆的东西我简要宣读以采取它为什么你会的。
编辑: 这本书 内部C# 从MS新闻。有锯片。提交人花了很多时间解释的事情是如何实现在CLR,如何语言翻译下集等。等。如果你能找到这本书它不是一个坏阅读。
编辑: 形式提供的链接,它看起来像
对象。GetHashCode()使用一个 内部领域的系统。目类产生的散列值。每 创建的对象是分配一个独特的对象的关键,储存作为一个整数,当它 被创建。这些钥匙从1开始,并增加每一次新的目的 任何类型的得到创造。
嗯我猜猜我需要写一些我自己的散列代码,如果我预期使用对象为哈希键。
所以也许它不会知道的散列编码的,它的"儿童"。
你的例子似乎证明,否则:-)的散列代码的关键 MyClass
和价值 1
是相同的两个 KeyValuePair
's。在型的执行必须使用它 Key
和 Value
对于其自己的散列码
动起来,这本字典类希望的唯一的钥匙。它是使用哈希码提供每个关键图的事情了。记住,运行时不叫 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;
}
}
基本上...在这种情况下,我如你想的两个对象是相同的距离,返回"真正的"为平等,但尚未返回不同的散列码。