一个受歧视的联盟如何覆盖.Equals()?

时间:2018-08-06 00:31:17

标签: f# override equality discriminated-union

我有一个已区别的联合体类型,想覆盖.Equals()

在这个简单的示例中,我可以为int使用.Equals函数来解决此问题,但是在我的代码中otherStuff不支持结构比较。

以下代码是我的最佳尝试:

[<CustomEquality>]
type ModelArg = { Name: string; OtherStuff: int}
    with override this.Equals (o: obj) = this.Name = (o :?> ModelArg).Name

然后我得到一条红色的波浪线和以下消息:

"The struct, record or union type 'ModelArg' has an explicit implementation of 'ObjectEquals'. Consider implementing a matching override for 'Object.GetHashCode'."

我想避免这样做,因为我实际上只关心字段Name,也是出于性能原因。

我当然可以编写一个equals函数,但不能与List这样的List.contains函数一起使用,我需要这样做。

有什么建议吗?

1 个答案:

答案 0 :(得分:4)

该错误告诉您,由于您要覆盖Equals方法,因此最好也覆盖GetHashCode

这样做的原因是,通常在.NET中(不仅仅是在F#中),哈希码通常用作等式的近似值。例如,如果要将对象放在哈希表中,则哈希表将根据GetHashCode在存储桶之间分配对象,并且也将以这种方式在存储桶中查找它们。然后,如果EqualsGetHashCode的实现方式不同,则哈希表的行为将不可预测-可能无法查找刚刚插入的对象或类似对象。

此外,错误消息并不建议您在相等性定义中包括int。它只说明您需要实现GetHashCode,并以与Equals实现相同的意义进行。只要您从不实际致电GetHashCode,这样做就不会对性能造成任何影响。如果可以,请参见上文。

由于您的所有Equals实现都比较了Name字段,因此也可以将GetHashCode委派给同一字段:

[<CustomEquality>]
type ModelArg = { Name: string; OtherStuff: int}
    with 
       override this.Equals (o: obj) = this.Name = (o :?> ModelArg).Name
       override this.GetHashCode() = this.Name.GetHashCode()

最后,当您使用Equals或其他类型的对象调用null时,实现会崩溃。如果您希望代码健壮,我建议您处理这种情况:

       override this.Equals (o: obj) = 
           match o with 
           | :? ModelArg as ma -> this.Name = ma.Name
           | _ -> false