Interface 在类型定义之外实现相等

Interface 在类型定义之外实现相等,interface,f#,operator-overloading,equality,Interface,F#,Operator Overloading,Equality,我有几种实现接口的类型。这些类型的相等性仅取决于接口成员。是否可以为这些类型定义一次相等,而不覆盖每种类型的Equals或op_equality 编辑 我尝试了以下方法,但是,无论出于何种原因,它都超越了=的所有用法,即使是对于没有实现方向性的类型也是如此 [<AutoOpen>] module Equality = let inline op_Equality (left:IEntity) (right:IEntity) = true [] 模相等= 让内联运算等于(左:

我有几种实现接口的类型。这些类型的相等性仅取决于接口成员。是否可以为这些类型定义一次相等,而不覆盖每种类型的
Equals
op_equality

编辑

我尝试了以下方法,但是,无论出于何种原因,它都超越了
=
的所有用法,即使是对于没有实现
方向性的类型也是如此

[<AutoOpen>]
module Equality =
    let inline op_Equality (left:IEntity) (right:IEntity) = true
[]
模相等=
让内联运算等于(左:IEntity)(右:IEntity)=真

我还尝试使用灵活的类型(
#IEntity
)。同样的结果。

我认为没有一种方法可以以静态的方式来实现这一点。问题是扩展成员(例如,如果您将
op_Equality
添加为扩展)被静态成员约束忽略(例如,如果您还使用
inlin
op_Equality
需求重新定义了
=

F#编译器只有在编译FSharp.Core.dll时才有一些特殊功能,这可能会有所帮助(搜索声明的源代码
让内联GenericOne
)。它使用类似于静态类型开关的东西——但这不能被普通人访问

所以,我没有比使用动态类型测试更好的想法,动态类型测试并不是一个好方法,而且最好定义一个自定义操作符来比较接口

作为参考,丑陋的动态方法是:

let inline (=) a b =
  match a, b with
  | :? IFoo as a, :? IFoo as b -> yourEquals a b
  | _ -> a = b

您试图做的是mixin或typeclass可能在其他语言中启用的事情;不幸的是,F#中没有等效的功能。您的最佳选择可能是以下选项之一:

  • 使用抽象基类而不是接口
  • 在类型之外编写相等方法,然后让所有实现都遵从它。比如说,

    let entityEquals (i1:IEntity) (i2:IEntity) =
      i1.Member1 = i2.Member1 &&
      i1.Member2 = i2.Member2 &&
      ...
    
    type MyEntity() =
      interface IEntity with
        member x.Member1 = ...
        ...
        override x.Equals(y) = 
          match y with
          | :? IEntity as y -> entityEquals x y
          | _ -> false
        override x.GetHashCode() =
          ...
    
    除了一些样板,这里的缺点是,如果其他任何人实现了您的
    IEntity
    接口,他们不会被迫使用您的平等方法-这是选择加入

  • 创建另一个运算符,用于
    IEntity
    s的相等性测试:

    let (==) (i1:IEntity) (i2:IEntity) =
      i1.Member1 = i2.Member1 &&
      ...
    
    这样做的(巨大)缺点是,包含
    IEntity
    s的类型(如元组、记录等)的结构相等性不会使用此运算符来比较这些组件,这可能会导致意外的代码中断


  • 我不知道如何使用接口来实现这一点。使用抽象基类很容易,但我想您已经意识到了这一点?是的。我一直在尝试静态成员约束,但没有找到一种方法将
    op_Equality
    限制到实现接口的类型。关于编辑:只能重载成员函数。我认为只能间接重载运算符,而且只能重载其中的一些。有关“+”运算符的说明,请参阅。但是,“=”不是以这种方式定义的。如果没有任何特殊情况适用,则定义为调用“Equals”。特殊情况的实现使用“编译器魔法”。@wmeyer是正确的-让绑定定义不重载。运算符可能最终通过静态成员约束调用重载的静态成员,但let绑定运算符本身并没有重载。我尝试使用
    内联
    实现相等,但不一定使用相等,但这是一个好的观点。我的目标是通过为实现接口的所有类型定义一次相等来实现传统的相等语义。我注意到
    mixin
    trait
    是保留字。有什么计划吗?特征,比如Scala(或者可能是一种改进的F#变体),将非常有用。