使用对象字段作为通用 Dictionary 键

如果我想使用对象作为 Dictionary的键,我需要重写哪些方法来使它们以特定的方式进行比较?

假设我有一个具有属性的类:

class Foo {
public string Name { get; set; }
public int FooID { get; set; }


// elided
}

我想创造一个:

Dictionary<Foo, List<Stuff>>

我希望具有相同 FooIDFoo对象被视为相同的组。我需要在 Foo类中重写哪些方法?

总结一下: 我想将 Stuff对象分类为按 Foo对象分组的列表。Stuff对象将有一个 FooID链接到它们的类别。

106273 次浏览

对于 Foo,您需要重写 object.GetHashCode ()和 object.Equals ()

字典将调用 GetHashCode ()为每个值计算一个散列桶,而 Equals 将比较两个 Foo 是否相同。

确保计算好的哈希代码(避免许多相等的 Foo 对象具有相同的哈希代码) ,但要确保两个相等的 Foo 具有相同的哈希代码。您可能希望从 Equals-Method 开始,然后(在 GetHashCode ()中) xor 比较 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();
}
}

默认情况下,两个重要的方法是 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;
}
}

最后,另一种方法是提供一个 IEqualityComparer<T>来做同样的事情。

因为您希望 FooID作为组的标识符,所以应该在 dictionary 中使用它作为键,而不是 Foo 对象:

Dictionary<int, List<Stuff>>

如果要使用 Foo对象作为键,那么只需实现 GetHashCodeEquals方法,以便只考虑 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());

Hashtable课怎么样!

Hashtable oMyDic = new Hashtable();
Object oAnyKeyObject = null;
Object oAnyValueObject = null;
oMyDic.Add(oAnyKeyObject, oAnyValueObject);
foreach (DictionaryEntry de in oMyDic)
{
// Do your job
}

通过以上方法,您可以使用任何对象(您的类对象)作为通用 Dictionary 键:)

我也有同样的问题。由于覆盖 Equals 和 GetHashCode,我现在可以使用任何尝试过的对象作为键。

下面是我用方法构建的一个类,这些方法可以在 Equals (object obj)和 GetHashCode ()的重写中使用。我决定使用泛型和散列算法,它们应该能够覆盖大多数对象。请让我知道,如果你在这里看到任何不适用于某些类型的对象,你有一个方法来改善它。

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