类别:
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();
交叉点是空列表,但其中应包含两个元素。
答案 0 :(得分:5)
IEqualityComparer
是您可以为Intersect
方法提供比较项目的界面。默认情况下,它不用于比较任何内容。所以你的代码只是使用Equals
中的内置Object
,除非对象是同一个对象,否则它将返回false。
您必须覆盖类中的默认Equal
和GetHashCode
方法,或者告诉交叉点使用比较器的实现。但是你不应该在数据存储类中实现比较器。
答案 1 :(得分:5)
您应该从对象:
覆盖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);
}
}
您还可以实施自定义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的评论中所提到的,您还应该重新考虑您的哈希代码实现。
答案 2 :(得分:2)
除非另有说明,否则Intersect
会使用EqualityComparer<Point>.Default
来object.Equals
和object.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
类本身上使用比较器,因为它看起来很难看,因为它看起来就像创建Point
一样用于比较的逻辑类。
来自MSDN:
答案 3 :(得分:2)
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());
}
}
尝试以上示例
答案 4 :(得分:1)
您应该将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
答案 5 :(得分:0)
您可以参考https://referencesource.microsoft.com/
找到<强>系统\ 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;
}
您的比较器实际上没有使用