.net 使用对象的字段作为通用字典键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/634826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 12:18:31  来源:igfitidea点击:

Using the field of an object as a generic Dictionary key

.netgenericsdictionary

提问by Dana

If I want to use objects as the keys for a Dictionary, what methods will I need to override to make them compare in a specific way?

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

Say I have a a class which has properties:

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

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

    // elided
} 

And I want to create a:

我想创建一个:

Dictionary<Foo, List<Stuff>>

I want Fooobjects with the same FooIDto be considered the same group. Which methods will I need to override in the Fooclass?

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

To summarize: I want to categorize Stuffobjects into lists, grouped by Fooobjects. Stuffobjects will have a FooIDto link them to their category.

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

回答by Marc Gravell

By default, the two important methods are GetHashCode()and Equals(). It is important that if two things are equal (Equals()returns true), that they have the same hash-code. For example, you might "return FooID;" as the GetHashCode()if you want that as the match. You can also implement IEquatable<Foo>, but that is optional:

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

Finally, another alternative is to provide an IEqualityComparer<T>to do the same.

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

回答by Guffa

As you want the FooIDto be the identifier for the group, you should use that as key in the dictionary instead of the Foo object:

由于您希望FooID成为组的标识符,您应该使用它作为字典中的键而不是 Foo 对象:

Dictionary<int, List<Stuff>>

If you would use the Fooobject as key, you would just implement the GetHashCodeand Equalsmethod to only consider the FooIDproperty. The Nameproperty would just be dead weight as far as the Dictionarywas concerned, so you would just use Fooas a wrapper for an int.

如果您将Foo对象用作键,则只需实现GetHashCodeandEquals方法以仅考虑该FooID属性。就 而言,该Name属性只是无用的Dictionary,因此您只需将其Foo用作int.

Therefore it's better to use the FooIDvalue directly, and then you don't have to implement anything as the Dictionaryalready supports using an intas a key.

因此最好FooID直接使用该值,然后您不必实现任何内容,因为Dictionary已经支持使用 anint作为键。

Edit:
If you want to use the Fooclass as key anyway, the IEqualityComparer<Foo>is easy to implement:

编辑:
如果您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; }
}

Usage:

用法:

Dictionary<Foo, List<Stuff>> dict = new Dictionary<Foo, List<Stuff>>(new FooEqualityComparer());

回答by froh42

For Foo you will need to override object.GetHashCode() and object.Equals()

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

The dictionary will call GetHashCode() to calculate a hash bucket for each value and Equals to compare whether two Foo's are identical.

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

Make sure to calculate good hash codes (avoid many equal Foo objects having the same hashcode), but make sure two equals Foos have the same hash code. You might want to start with the Equals-Method and then (in GetHashCode()) xor the hash code of every member you compare in Equals.

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

回答by Behzad Ebrahimi

What about Hashtableclass!

什么Hashtable类的!

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

In above way, you can use any object (your class object) as a generic Dictionary key :)

通过上述方式,您可以使用任何对象(您的类对象)作为通用字典键:)

回答by kb4000

I had the same problem. I can now use any object I've tried as a key due to overriding Equals and GetHashCode.

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

Here is a class that I built with methods to use inside of the overrides of Equals(object obj) and GetHashCode(). I decided to use generics and a hashing algorithm that should be able to cover most objects. Please let me know if you see anything here that doesn't work for some types of object and you have a way to improve it.

这是我使用在 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;
    }
}

Here is how it's used in a class:

以下是它在类中的使用方式:

public override bool Equals(object obj)
    {
        return new Equality<ClassName>().Equals(this, obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return new Equality<ClassName>().GetHashCode(this);
        }
    }