简体   繁体   English

两点的C#集合不返回任何结果

[英]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());
    }
}

I am trying to find common Points (intersection) in two collections, but the result is empty collection - two elements should be in it. 我试图在两个集合中找到公共点(交点),但结果是空集合-两个元素应该在其中。 Why? 为什么? I've implemented IEqualityComparer. 我已经实现了IEqualityComparer。 Did I did something wrong? 我做错什么了吗?

Example collections: 示例集合:

  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();

Intersection is empty list, but two elements should be in it. 交集为空列表,但其中应包含两个元素。

IEqualityComparer is an interface you can give to the Intersect method to compare items. IEqualityComparer是您可以提供给Intersect方法以比较项目的接口。 It is not used by default to compare anything. 默认情况下不使用它来进行任何比较。 So your code is just using the built-in Equals in Object which will return false unless the objects are the same object. 因此,您的代码仅使用内置的Object中的Equals ,除非对象是同一对象,否则它将返回false。

You have to either override the default Equal and GetHashCode methods in the class or tell the intersection to use your implementation of the comparer. 您必须重写类中的默认EqualGetHashCode方法,或者告诉交叉点使用比较器的实现。 But you shouldn't implement the comparer in a data storage class. 但是,您不应该在数据存储类中实现比较器。

You should override Equals and GetHashCode from object: 您应该从对象覆盖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);
    }
}

You could also implement a custom IEqualityComparer and pass it to intersect : 您还可以实现自定义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();

As mentioned in the comments by @decPL, you should also reconsider your hash code implementation. 如@decPL的注释中所述,您还应该重新考虑哈希代码的实现。

Unless specified, Intersect uses EqualityComparer<Point>.Default which will use the object.Equals and object.GetHashCode methods for comparison (they will just check if the reference is the same); 除非指定,否则Intersect使用EqualityComparer<Point>.Default它将使用object.Equalsobject.GetHashCode方法用于比较(它们将只检查如果参考是相同的);

to make it work, pass the comparer to the method: 为了使其工作,将比较器传递给该方法:

  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();

Although, ideally for the SRP you shouldn't have the comparer on the Point class itself as it would look hacky as it looks above creating a Point just as logic class for comparison. 虽然,对于SRP而言 ,理想情况下,您不应该在Point类本身上使用比较器,因为在创建Point类作为比较逻辑类的上方时,它看起来很笨拙。

From MSDN: 从MSDN:

EqualityComparer 平等比较器

Intersect 相交

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());
    }
}

Try above example 试试上面的例子

You should separate your Point and PointComparer classes. 您应该将Point和PointComparer类分开。

The manual has good example: 该手册有一个很好的例子:

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 https://msdn.microsoft.com/zh-CN/library/bb460136(v=vs.110).aspx

As you can find on reference https://referencesource.microsoft.com/ 正如您可以在参考资料中找到的https://referencesource.microsoft.com/

System\\Linq\\Enumerable.cs 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;
        }

Your comparer actually doesn't used 您的比较器实际上没有使用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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