HashSet.CreateSetComparer() cannot specify IEqualityComparer, is there an alternative?

前端 未结 3 542
广开言路
广开言路 2021-01-14 14:13

In the internal source there is such a constructor public HashSetEqualityComparer(IEqualityComparer comparer) but it\'s internal so I can\'t use it.<

3条回答
  •  不知归路
    2021-01-14 14:51

    I agree @InBetween, using SetEquals is the best way. Even if add the constructor still can not achieve what you want.

    please see this code: http://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs,1360

    Here is I try to do:

    class HashSetEqualityComparerWrapper : IEqualityComparer>
    {
        static private Type HashSetEqualityComparerType = HashSet.CreateSetComparer().GetType();
        private IEqualityComparer> _comparer;
    
        public HashSetEqualityComparerWrapper()
        {
            _comparer = HashSet.CreateSetComparer();
        }
        public HashSetEqualityComparerWrapper(IEqualityComparer comparer)
        {
            _comparer = HashSet.CreateSetComparer();
            if (comparer != null)
            {
                FieldInfo m_comparer_field = HashSetEqualityComparerType.GetField("m_comparer", BindingFlags.NonPublic | BindingFlags.Instance);
                m_comparer_field.SetValue(_comparer, comparer);
            }
        }
    
        public bool Equals(HashSet x, HashSet y)
        {
            return _comparer.Equals(x, y);
        }
        public int GetHashCode(HashSet obj)
        {
            return _comparer.GetHashCode(obj);
        }
    }
    

    UPDATE

    I took 5 mins to implement another version form HashSetEqualityComparer source code. And rewrite the bool Equals(HashSet x, HashSet y) method. It is not complex. All code just copy and paste from source, I just revise a bit.

    class CustomHashSetEqualityComparer : IEqualityComparer>
    {
        private IEqualityComparer m_comparer;
    
        public CustomHashSetEqualityComparer()
        {
            m_comparer = EqualityComparer.Default;
        }
    
        public CustomHashSetEqualityComparer(IEqualityComparer comparer)
        {
            if (comparer == null)
            {
                m_comparer = EqualityComparer.Default;
            }
            else
            {
                m_comparer = comparer;
            }
        }
    
        // using m_comparer to keep equals properties in tact; don't want to choose one of the comparers
        public bool Equals(HashSet x, HashSet y)
        {
            // http://referencesource.microsoft.com/#System.Core/System/Collections/Generic/HashSet.cs,1360
            // handle null cases first
            if (x == null)
            {
                return (y == null);
            }
            else if (y == null)
            {
                // set1 != null
                return false;
            }
    
            // all comparers are the same; this is faster
            if (AreEqualityComparersEqual(x, y))
            {
                if (x.Count != y.Count)
                {
                    return false;
                }
            }
            // n^2 search because items are hashed according to their respective ECs
            foreach (T set2Item in y)
            {
                bool found = false;
                foreach (T set1Item in x)
                {
                    if (m_comparer.Equals(set2Item, set1Item))
                    {
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    return false;
                }
            }
            return true;
        }
    
        public int GetHashCode(HashSet obj)
        {
            int hashCode = 0;
            if (obj != null)
            {
                foreach (T t in obj)
                {
                    hashCode = hashCode ^ (m_comparer.GetHashCode(t) & 0x7FFFFFFF);
                }
            } // else returns hashcode of 0 for null hashsets
            return hashCode;
        }
    
        // Equals method for the comparer itself. 
        public override bool Equals(Object obj)
        {
            CustomHashSetEqualityComparer comparer = obj as CustomHashSetEqualityComparer;
            if (comparer == null)
            {
                return false;
            }
            return (this.m_comparer == comparer.m_comparer);
        }
    
        public override int GetHashCode()
        {
            return m_comparer.GetHashCode();
        }
    
        static private bool AreEqualityComparersEqual(HashSet set1, HashSet set2)
        {
            return set1.Comparer.Equals(set2.Comparer);
        }
    }
    

提交回复
热议问题