【问题标题】:Using equality comparer in lambda expression在 lambda 表达式中使用相等比较器
【发布时间】:2015-01-17 21:47:38
【问题描述】:

我有一个 Venue 类和一个像这样的 Coordinate 类:

class Venue
{
    string Name;
    Coordinate coordinate;
}

class Coordinate
{
    double latitute;
    double longitude;
}

现在,我希望能够根据如下坐标选择场地:

List<Venue> venues = GetAllVenues();

var myVenue = venues.FirstOrDefault(venue=>venue.coordinate == myCoordinate);

我有一个 IEqualityComparer 实现,但 lambda 表达式没有将 IEqualityComparer 作为参数的重载。

如何在 lambda 表达式中使用我的相等比较器?

编辑:

我的相等比较器如下所示:

class CoordinatesEqualityComparer:IEqualityComparer<Coordinate>
    {
        public bool Equals(Coordinate x, Coordinate y)
        {
            return x.RowIndex == y.RowIndex && x.ColumnIndex == y.ColumnIndex;
        }

        public int GetHashCode(Coordinate obj)
        {
            return obj.GetHashCode();
        }
    }

当我像这样执行 Union() 操作时,即使两个列表中的坐标相同,它也无法正常工作。

List<Coordinates> coordinates; 
CoordinatesEqualityComparer comparer; 
coordinates.Union(someOtherListOfCoordinates, comparer); 

但是,当我与自身进行联合时,它会起作用。我究竟做错了什么?它与 GetHashCode() 实现有关吗?

编辑 2: 修复 GetHashCode() 方法似乎可以解决问题。

public int GetHashCode(Coordinates obj)
        {
            // Warning:Hack. Use two prime numbers to generate a hash based on two properties.
            return obj.RowIndex.GetHashCode() * 7 + obj.ColumnIndex.GetHashCode() * 13 ;
        }

【问题讨论】:

    标签: c# .net


    【解决方案1】:

    你试过了吗:

    var ec = new YourEqualityComparer();
    var myVenue = venues.FirstOrDefault(venue => 
                                             ec.Equals(venue.coordinate, myCoordinate));
    




    当然,另一种方法是为您的 Coordinate 类定义 == 运算符,然后您就不需要 IEqualityComparer

    class Coordinate
    {
        double latitude;
        double longitude;
    
        public override bool Equals(object obj)
        {
            return Object.ReferenceEquals(this, obj)) ||
                   this == (other as Coordinate);
        }
    
        public static bool operator ==(Coordinate l, Coordinate r)
        {
            return ((object)l == null && (object)r == null) || 
                   ((object)l != null && (object)r != null) &&
                   // equality check including epsilons, edge cases, etc.
        }
    
        public static bool operator !=(Coordinate l, Coordinate r)
        {
            return !(l == r);
        }
    }
    

    【讨论】:

    • 您对== 的实现已损坏。它在l == null 上调用自己,从那里进入无限递归,这将永远持续(如果发生一些尾调用优化)或溢出调用堆栈。考虑(object)l == (object)null
    • @JeppeStigNielsen 我试图不陷入极端情况,因为这与我的示例无关,但他们还是找到了我。现在应该可以了。感谢您指出这一点。
    【解决方案2】:

    我会像这样实现IEquatable&lt;Coordinate&gt;、覆盖 Equals(object)、覆盖 GetHashCode() 和 == != 运算符:

    public class Coordinate : IEquatable<Coordinate>
    {
        public double Latitide { get; set; }
        public double Longitude { get; set; }
    
        public bool Equals(Coordinate other)
        {
            if (other == null)
            {
                return false;
            }
            else
            {
                return this.Latitide == other.Latitide && this.Longitude == other.Longitude;
            }
        }
    
        public override bool Equals(object obj)
        {
            return this.Equals(obj as Coordinate);
        }
    
        public override int GetHashCode()
        {
            return this.Latitide.GetHashCode() ^ this.Longitude.GetHashCode();
        }
    
        public static bool operator ==(Coordinate value1, Coordinate value2)
        {
            if (!Object.ReferenceEquals(value1, null) && Object.ReferenceEquals(value2, null))
            {
                return false;
            }
            else if (Object.ReferenceEquals(value1, null) && !Object.ReferenceEquals(value2, null))
            {
                return false;
            }
            else if (Object.ReferenceEquals(value1, null) && Object.ReferenceEquals(value2, null))
            {
                return true;
            }
            else
            {
                return value1.Latitide == value2.Latitide && value1.Longitude == value2.Longitude;
            }
        }
    
        public static bool operator !=(Coordinate value1, Coordinate value2)
        {
            return !(value1 == value2);
        }
    }
    

    【讨论】:

    • 请注意,您可能会在 operator == 等中出现堆栈溢出,因为它试图通过检查 null 来调用自身。我通常使用 Object.ReferenceEquals(obj,null) 来避免这种情况。
    • 感谢您的信息。如果您覆盖 == 和 != 运算符,则您是正确的,您需要执行 Object.ReferenceEquals。
    猜你喜欢
    • 2014-08-28
    • 1970-01-01
    • 1970-01-01
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 2020-02-08
    • 2012-09-11
    • 2017-10-07
    相关资源
    最近更新 更多