C# 收集两个点没有 return 任何结果

C# collection of two points doesn't return any results

Class:

public class Point : IEqualityComparer<Point>
{               
    public char HorizontalPosition { get; set; }
    public int VerticalPosition { get; set; }

    public Point(char horizontalPosition, int verticalPosition)
    {
        HorizontalPosition = char.ToUpper(horizontalPosition);
        VerticalPosition = verticalPosition;           
    }   

    public bool Equals(Point x, Point y)
    {
        return (x.VerticalPosition == y.VerticalPosition && x.HorizontalPosition == y.HorizontalPosition);
    }

    public int GetHashCode(Point obj)
    {
        return (obj.HorizontalPosition.GetHashCode() + obj.VerticalPosition.GetHashCode());
    }
}

我试图在两个集合中找到公共点(交点),但结果是空集合 - 其中应该有两个元素。为什么?我已经实施了 IEqualityComparer。我是不是做错了什么?

示例集合:

  List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
  List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
  List<Point> intersection = first.Intersect(second).ToList();

交集是空列表,但其中应该有两个元素。

IEqualityComparer 是一个接口,您可以为 Intersect 方法提供比较项的接口。默认情况下不使用它来比较任何东西。因此,您的代码仅使用 Object 中的内置 Equals,除非对象是同一对象,否则它将 return false。

您必须覆盖 class 中的默认 EqualGetHashCode 方法,或者告诉交集使用您的比较器实现。但是您不应该在数据存储 class.

中实现比较器

您应该从对象覆盖 EqualsGetHashCode

public class Point
{
    public char HorizontalPosition { get; set; }
    public int VerticalPosition { get; set; }

    public Point(char horizontalPosition, int verticalPosition)
    {
        HorizontalPosition = char.ToUpper(horizontalPosition);
        VerticalPosition = verticalPosition;
    }

    public override int GetHashCode()
    {
        unchecked
        { 
            return (HorizontalPosition * 397) ^ VerticalPosition;
        }
    }

    protected bool Equals(Point other)
    {
        return Equals(HorizontalPosition, other.HorizontalPosition) && Equals(VerticalPosition, other.VerticalPosition);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Point)obj);
    }
}

您还可以实现自定义 IEqualityComparer 并将其传递给 intersect:

public class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point a, Point b)
    {
        return a.HorizontalPosition == b.HorizontalPosition && a.VerticalPosition == b.VerticalPosition;
    }

    public int GetHashCode(Point p)
    {
        unchecked
        { 
            return (p.HorizontalPosition * 397) ^ p.VerticalPosition;
        }
    }
}

// ...

List<Point> intersection = first.Intersect(second, new PointComparer()).ToList();

正如@decPL 在评论中提到的,您还应该重新考虑您的哈希码实现。

除非指定,否则Intersect使用EqualityComparer<Point>.Default,它将使用object.Equalsobject.GetHashCode方法进行比较(它们只会检查引用是否相同);

要使其工作,请将比较器传递给方法:

  List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
  List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
  List<Point> intersection = first.Intersect(second, new Point('a', 0)).ToList();

虽然,理想情况下 SRP 你不应该在 Point class 本身上有比较器,因为它看起来像上面那样看起来很老套创建一个 Point 作为逻辑 class 进行比较。

来自 MSDN:

EqualityComparer

Intersect

您应该将 Point 和 PointComparer 分开 类。

手册中有很好的例子:

public class ProductA
{ 
    public string Name { get; set; }
    public int Code { get; set; }
}

public class ProductComparer : IEqualityComparer<ProductA>
{

    public bool Equals(ProductA x, ProductA y)
    {
        //Check whether the objects are the same object. 
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether the products' properties are equal. 
        return x != null && y != null && x.Code.Equals(y.Code) && x.Name.Equals(y.Name);
    }

    public int GetHashCode(ProductA obj)
    {
        //Get hash code for the Name field if it is not null. 
        int hashProductName = obj.Name == null ? 0 : obj.Name.GetHashCode();

        //Get hash code for the Code field. 
        int hashProductCode = obj.Code.GetHashCode();

        //Calculate the hash code for the product. 
        return hashProductName ^ hashProductCode;
    }
}

https://msdn.microsoft.com/en-us/library/bb460136(v=vs.110).aspx

List<Point> first = new List<Point> { new Point('a', 1), new Point('b', 2) };
            List<Point> second = new List<Point> { new Point('a', 1), new Point('b', 2) };
            List<Point> intersection = first.Intersect(second, new PointComparer()).ToList();


public class Point 
{
    public char HorizontalPosition { get; set; }
    public int VerticalPosition { get; set; }

    public Point(char horizontalPosition, int verticalPosition)
    {
        HorizontalPosition = char.ToUpper(horizontalPosition);
        VerticalPosition = verticalPosition;
    }
}

public class PointComparer : IEqualityComparer<Point>
{
    public bool Equals(Point x, Point y)
    {
        return (x.VerticalPosition == y.VerticalPosition && x.HorizontalPosition == y.HorizontalPosition);
    }

    public int GetHashCode(Point obj)
    {
        return (obj.HorizontalPosition.GetHashCode() + obj.VerticalPosition.GetHashCode());
    }
}

试试上面的例子

您可以在参考资料中找到 https://referencesource.microsoft.com/

System\Linq\Enumerable.cs

    public static IEnumerable<TSource> Intersect<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) {
                if (first == null) throw Error.ArgumentNull("first");
                if (second == null) throw Error.ArgumentNull("second");
                return IntersectIterator<TSource>(first, second, null);
            }

...
    static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
        {
            Set<TSource> set = new Set<TSource>(comparer);
            foreach (TSource element in second) set.Add(element);
            foreach (TSource element in first)
                if (set.Remove(element)) yield return element;
        }
...
// If value is in set, remove it and return true; otherwise return false
        public bool Remove(TElement value) {
            int hashCode = InternalGetHashCode(value);
            int bucket = hashCode % buckets.Length;
            int last = -1;
            for (int i = buckets[bucket] - 1; i >= 0; last = i, i = slots[i].next) {
                if (slots[i].hashCode == hashCode && comparer.Equals(slots[i].value, value)) {
                    if (last < 0) {
                        buckets[bucket] = slots[i].next + 1;
                    }
                    else {
                        slots[last].next = slots[i].next;
                    }
                    slots[i].hashCode = -1;
                    slots[i].value = default(TElement);
                    slots[i].next = freeList;
                    freeList = i;
                    return true;
                }
            }
            return false;
        }

你的比较器实际上没有用