简体   繁体   中英

ContainsKey in dictionary of hashset<myClass>

I have a dictionary:

Dictionary<HashSet<myClass>, List<MyObj>> myDict = ...

And I have:

HashSet<myClass> myHashSet = ...

I want to check if the dictionary (myDict) contains myHashSet.

I tried to override two methods:

1) equal

2) GetHashCode

public class myClass
{
    public string id;
    public int number;

    public override bool Equals(object obj)
    {
        myClass other = obj as myClass;
        bool ret = false;
        if (other != null)
        {
            ret = (this.number == other.number) && (this.id == other.id);
        }
        return ret;
    }

    public override int GetHashCode()
    {
        return this.number ^ this.id.GetHashCode();
    }
};

Unfortunately, a key that is found in dictionary, returns false for the code: myDict.ContainsKey(myHashSet)

Any help appreciated!

Just because you overrode myClass 's Equals( and GetHashCode() does not mean that that you overrode HashSet<myClass> 's Equals( and GetHashCode() , that is what is being used when you do the dictionary lookup.

If you want it to work you need to pass a IEqualityComparer<HashSet<myClass>> in to the constructor of the dictionary so it will use that comparer when doing the dictionary lookup.

public class myClassSetComperer : IEqualityComparer<HashSet<myClass>>
{
    public bool Equals(HashSet<myClass> x, HashSet<myClass> y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (ReferenceEquals(null, x)) return false;
        if (ReferenceEquals(null, y)) return false;
        return x.SetEquals(y);
    }

    public int GetHashCode(HashSet<myClass> obj)
    {
        unchecked
        {
            int x = 0;
            foreach (var myClass in obj)
            {
                x = (x*397) ^ myClass?.GetHashCode() ?? 0;
            }
            return x;
        }
    }
}

//elsewhere
Dictionary<HashSet<myClass>, List<MyObj>> myDict = new Dictionary<HashSet<myClass>, List<MyObj>>(new myClassSetComperer());

VERY IMPORTANT NOTE : Dictionary keys (and hash sets) break horribly if you do anything that causes Equals( or GetHashCode() to change once put in as the lookup key. If you modify the HashSet<myClass> or one of the myClass objects after you put it in the dictionary you will break the dictionary and potentially the HashSet. See this very good blog post by Eric Lippert on "Guidelines and rules for GetHashCode"

overriding the GetHasCode and Equal is when comparing instances of myClass.

Here's a sample of using ContainsKey, which is checking by objects reference.

Dictionary<HashSet<string>, List<string>> hashSetDictionary = new Dictionary<HashSet<string>, List<string>>();
            var myHashSet = new HashSet<string>();

            hashSetDictionary.Add(myHashSet, null);
            Console.WriteLine(hashSetDictionary.ContainsKey(myHashSet));

Here's an update to your code

 public class myClass
    {
        public myClass(string text, int num)
        {
            this.Text = text;
            this.Num = num;
        }

        public string Text { get; set; }
        public int Num { get; set; }
    }

    public class MyObj { }

    public class AlwaysTrueHashSet<T> : HashSet<T>
    {
        public override bool Equals(object obj)
        {
            return this.GetHashCode() == obj.GetHashCode();
        }

        public override int GetHashCode()
        {
            return "Counting hashcode".GetHashCode();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {


            Dictionary<HashSet<myClass>, List<MyObj>> myDict = new Dictionary<HashSet<myClass>,


            List<MyObj>>();
            var myHashSet1 = new AlwaysTrueHashSet<myClass>();
            myHashSet1.Add(new myClass("123", 5));
            myDict.Add(myHashSet1, null);



            var myHashSet2 = new AlwaysTrueHashSet<myClass>();
            myHashSet2.Add(new myClass("123", 5));

            /*
             * when containsKey is invoked, it's checking if the reference of myHashSet2 is the same as myHashSet1.
             * That's the default behavior.
             * 
             * extend HashSet, and override the gethashcode and equal methods  
             */
            if (myDict.ContainsKey(myHashSet2))
            {
                Console.WriteLine("in");
                int i = 3; // it doesn't get this line }  
            }
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM