it-swarm.asia

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

如果我想使用对象作为Dictionary的键,我需要覆盖哪些方法才能以特定方式进行比较?

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

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

    // elided
} 

我想创建一个:

Dictionary<Foo, List<Stuff>>

我想Foo具有相同FooID的对象被认为是同一组。我需要在Foo类中覆盖哪些方法?

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

111
Dana

默认情况下,两个重要的方法是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>来做同样的事情。

139
Marc Gravell

如您所希望FooID是该组的标识符,您应该将其用作字典中的键而不是Foo对象:

Dictionary<int, List<Stuff>>

如果要使用Foo对象作为键,则只需实现GetHashCodeEquals方法以仅考虑FooID属性。就Name而言,Dictionary属性只是自重,因此您只需使用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());
30
Guffa

对于Foo,您需要覆盖object.GetHashCode()和object.Equals()

字典将调用GetHashCode()来计算每个值的哈希桶,并使用Equals来比较两个Foo是否相同。

确保计算好的哈希码(避免许多具有相同哈希码的相等Foo对象),但要确保两个等于Foos具有相同的哈希码。您可能希望从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();
     }
}
8
froh42

怎么样Hashtable class!

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

在上面的方式中,您可以使用任何对象(您的类对象)作为通用的Dictionary键:)

0
Behzad Ebrahimi

我有同样的问题。由于重写了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);
        }
    }
0
kb4000