使用一个对象作为通用的Dictionary键
如果我想使用对象作为Dictionary
的关键字,我需要重写哪些方法才能以特定方式进行比较?
说我有一个有属性的类:
class Foo { public string Name { get; set; } public int FooID { get; set; } // elided }
我想创build一个:
Dictionary<Foo, List<Stuff>>
我希望具有相同FooID
Foo
对象被视为相同的组。 我需要在Foo
类中重写哪些方法?
总结一下:我想将Stuff
对象分类成Foo
对象分组的列表。 Stuff
对象将有一个FooID
链接到他们的类别。
默认情况下,两个重要的方法是GetHashCode()
和Equals()
。 如果两个事物相等( Equals()
返回true),它们具有相同的散列码是很重要的。 例如,你可能会“返回FooID” 作为GetHashCode()
如果你想作为匹配。 你也可以实现IEquatable<Foo>
,但这是可选的:
class Foo : IEquatable<Foo> { public string Name { get; set;} public int FooID {get; set;} public override int GetHashCode() { return FooID; } public override bool Equals(object obj) { return Equals(obj as Foo); } public bool Equals(Foo obj) { return obj != null && obj.FooID == this.FooID; } }
最后,另一种select是提供一个IEqualityComparer<T>
来做同样的事情。
正如你所希望的FooID
是组的标识符,你应该使用它作为字典中的键而不是Foo对象:
Dictionary<int, List<Stuff>>
如果将Foo
对象用作键,则只需实现GetHashCode
和Equals
方法即可仅考虑FooID
属性。 对于Dictionary
而言, Name
属性只是无用的权重,所以您只需使用Foo
作为int
的包装。
因此,最好直接使用FooID
值,然后不必实现任何东西,因为Dictionary
已经支持使用int
作为键。
编辑:
如果你想使用Foo
类作为关键字, IEqualityComparer<Foo>
很容易实现:
public class FooEqualityComparer : IEqualityComparer<Foo> { public int GetHashCode(Foo foo) { return foo.FooID.GetHashCode(); } public bool Equals(Foo foo1, Foo foo2) { return foo1.FooID == foo2.FooID; } }
用法:
Dictionary<Foo, List<Stuff>> dict = new Dictionary<Foo, List<Stuff>>(new FooEqualityComparer());
对于Foo,您将需要重写object.GetHashCode()和object.Equals()
字典将调用GetHashCode()来计算每个值的哈希桶,等于比较两个Foo是否相同。
确保计算好的散列码(避免许多相同的Foo对象具有相同的散列码),但要确保两个等于Foos具有相同的散列码。 您可能需要从Equals-Method开始,然后(在GetHashCode())中使用您在Equals中比较的每个成员的哈希码。
public class Foo { public string A; public string B; override bool Equals(object other) { var otherFoo = other as Foo; if (otherFoo == null) return false; return A==otherFoo.A && B ==otherFoo.B; } override int GetHashCode() { return 17 * A.GetHashCode() + B.GetHashCode(); } }
什么关于Hashtable
类!
Hashtable oMyDic = new Hashtable(); Object oAnyKeyObject = null; Object oAnyValueObject = null; oMyDic.Add(oAnyKeyObject, oAnyValueObject); foreach (DictionaryEntry de in oMyDic) { // Do your job }
在上面的方式,你可以使用任何对象(你的类对象)作为一个通用的字典键:)
我有同样的问题。 我现在可以使用任何我试过的对象作为关键字,因为重写了Equals和GetHashCode。
这是一个我用方法构build的类,在Equals(object obj)和GetHashCode()的覆盖内部使用。 我决定使用generics和哈希algorithm,应该能够覆盖大部分对象。 请让我知道,如果你在这里看到的东西不适用于某些types的对象,你有办法改善它。
public class Equality<T> { public int GetHashCode(T classInstance) { List<FieldInfo> fields = GetFields(); unchecked { int hash = 17; foreach (FieldInfo field in fields) { hash = hash * 397 + field.GetValue(classInstance).GetHashCode(); } return hash; } } public bool Equals(T classInstance, object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (classInstance.GetType() != obj.GetType()) { return false; } return Equals(classInstance, (T)obj); } private bool Equals(T classInstance, T otherInstance) { List<FieldInfo> fields = GetFields(); foreach (var field in fields) { if (!field.GetValue(classInstance).Equals(field.GetValue(otherInstance))) { return false; } } return true; } private List<FieldInfo> GetFields() { Type myType = typeof(T); List<FieldInfo> fields = myType.GetTypeInfo().DeclaredFields.ToList(); return fields; } }
这是如何在一个类中使用的:
public override bool Equals(object obj) { return new Equality<ClassName>().Equals(this, obj); } public override int GetHashCode() { unchecked { return new Equality<ClassName>().GetHashCode(this); } }