F#重载=与<>运算符判断结果不符合预期问题求解
F# 相等运算符重载问题修复
问题现象
- 重载
=、<>运算符时无法直接使用运算符符号作为成员名,收到编译器提示:- 不应将名称
'(=)'用作成员名。若要为类型定义相等语义,请重写Object.Equals成员;如果要定义供其他CLI语言使用的静态成员,请改用名称op_Equality。 - 不应将名称
'(<>)'用作成员名。若要为类型定义相等语义,请重写Object.Equals成员;如果要定义供其他CLI语言使用的静态成员,请改用名称op_Inequality。
- 不应将名称
- 按提示定义
op_Equality、op_Inequality静态成员后编译通过,但运行结果不符合预期:实例化两个坐标均为(1, 0)的Point对象p1、p2时,p1 = p2返回false,p1 <> p2返回true。
原始错误实现代码:
open System type Point(X : float, Y : float) = class member public this.X : float = X member public this.Y : float = Y static member public Origin : Point = Point(0.0, 0.0) static member public (+) (P1 : Point, P2 : Point) : Point = Point(P1.X+P2.X, P1.Y+P2.Y) static member public (-) (P1 : Point, P2 : Point) : Point = Point(P1.X-P2.X, P1.Y-P2.Y) static member public op_Equality (P1 : Point, P2 : Point) : bool = P1.X = P2.X && P1.Y = P2.Y static member public op_Inequality (P1 : Point, P2 : Point) : bool = P1.X <> P2.X && P1.Y <> P2.Y override this.ToString() = String.Format("({0}, {1})", X, Y) end
错误原因
- 逻辑错误:
op_Inequality实现中错误使用了&&(逻辑与)运算符,当前逻辑要求X、Y坐标同时不相等才判定对象不等,正确逻辑应为只要任意一个坐标不相等就判定对象不等。 - 语义不符合F#规则:编译器提示已经明确说明,F#中类型的相等语义优先通过重写
Object.Equals实现,仅定义op_Equality静态成员不会覆盖F#原生=运算符的默认行为。当前=返回false是因为类类型默认做引用相等判断,两个分别实例化的对象引用地址不同,哪怕值一致也会被判定为不等。
修复方案
按F#的相等语义规范实现即可,需要完成三个修改:
- 重写
Object.Equals方法,基于坐标值实现值相等判断 - 重写
Object.GetHashCode方法,保证相等对象返回相同哈希码,避免哈希集合类操作出现异常 - 修正不等判断逻辑,
op_Equality和op_Inequality直接复用Equals逻辑,避免重复代码写错判断条件
修复后的完整代码:
open System type Point(X : float, Y : float) = member public this.X : float = X member public this.Y : float = Y static member public Origin : Point = Point(0.0, 0.0) static member public (+) (P1 : Point, P2 : Point) : Point = Point(P1.X+P2.X, P1.Y+P2.Y) static member public (-) (P1 : Point, P2 : Point) : Point = Point(P1.X-P2.X, P1.Y-P2.Y) override this.Equals(obj) = match obj with | :? Point as other -> this.X = other.X && this.Y = other.Y | _ -> false override this.GetHashCode() = HashCode.Combine(this.X, this.Y) static member op_Equality (P1 : Point, P2 : Point) = P1.Equals(P2) static member op_Inequality (P1 : Point, P2 : Point) = not (P1.Equals(P2)) override this.ToString() = String.Format("({0}, {1})", X, Y)
验证结果
执行测试代码:
let p1 = Point(1, 0) let p2 = Point(1, 0) printfn $"%b{p1 = p2}" // 输出 true printfn $"%b{p1 <> p2}" // 输出 false
结果符合预期。
内容的提问来源于stack exchange,提问作者user17682078
相关产品推荐
相关产品推荐

