[英]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. 您必须重写类中的默认Equal
和GetHashCode
方法,或者告诉交叉点使用比较器的实现。 But you shouldn't implement the comparer in a data storage class. 但是,您不应该在数据存储类中实现比较器。
You should override Equals
and GetHashCode
from object: 您应该从对象覆盖Equals
和GetHashCode
:
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.Equals
和object.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:
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.