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

前端 未结 3 540
广开言路
广开言路 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:40

    Avoid this class if you use custom comparers. It uses its own equality comparer to perform GetHashCode, but when performing Equals(Set1, Set2) if Set1 and Set2 have the same equality comparer, the the HashSetEqualityComparer will use the comparer of the sets. HashsetEqualityComparer will only use its own comparer for equals if Set1 and Set2 have different comparers

    It gets worse. It calls HashSet.HashSetEquals, which has a bug in it (See https://referencesource.microsoft.com/#system.core/System/Collections/Generic/HashSet.cs line 1489, which is missing a if (set1.Count != set2.Count) return false before performing the subset check.

    The bug is illustrated by the following program:

    class Program
    {
        private class MyEqualityComparer : EqualityComparer
        {
            public override bool Equals(int x, int y)
            {
                return x == y;
            }
    
            public override int GetHashCode(int obj)
            {
                return obj.GetHashCode();
            }
        }
    
        static void Main(string[] args)
        {
            var comparer = HashSet.CreateSetComparer();
            var set1 = new HashSet(new MyEqualityComparer()) { 1 };
            var set2 = new HashSet { 1, 2 };
    
            Console.WriteLine(comparer.Equals(set1, set2));
            Console.WriteLine(comparer.Equals(set2, set1)); //True!
    
            Console.ReadKey();
        }
    }
    

    Regarding other answers to this question (I don't have the rep to comment):

    • Wilhelm Liao: His answer also contains the bug because it's copied from the reference source
    • InBetween: The solution is not symmetric. CustomHashSetEqualityComparer.Equals(A, B) does not always equals CustomHashSetEqualityComparer.Equals(B, A). I would be scared of that.

    I think a robust implementation should throw an exception if it encounters a set which has a different comparer to its own. It could always use its own comparer and ignore the set comparer, but that would give strange and unintuitive behaviour.

提交回复
热议问题