使用CustomEquality和CustomComparison在DU中重写Equals等的正确方法?

时间:2014-10-25 17:35:49

标签: f#

我遇到过一种情况,我喜欢比较一个有一个案例的歧视联盟,这个案例不能直接比较,而且我不需要在(自定义)比较中使用。出于这个原因,我写了CustomEqualityCustomComparison函数。唉,我得到了一个stackoverflow,一般来说,我不确定如何在F#中处理这种相等覆盖情况。我通过 Don Syme Chris Smith Equality and Comparison Constraints in F#阅读F# Language Details (Gotchas)

< edit:正如评论中所指出的那样,对于Funky案例,应该做些什么?我对How do I create a job queue using a MailboxProcessor?提出了类似的想法,其中我列出了可用于执行功能或控制状态机的DU。

看一下其他帖子(我不在这里制作代码),优雅地使用MailboxProcessor我看到它有类似的问题和一些类似的想法。我不确定设计是否会更好,或者是否有比较功能的方法,无论如何我还有更多问题(使用C#背景)。

问题:

  • 如何避免Equals中的堆栈溢出? (可能很简单,但是......)
  • 通用IComparable应该实施吗?
  • 根据Chris' static member (=)等应该超载博客文章?
  • 在类似情况下,DUs还应该超载什么?如果有性能实现,是否应该实现IEquatable

我认为其中一些可以通过查看IL来回答(例如,如果编译器已经为DUs生成IEquatable),只是为了确保我没有忽略任何内容。

代码

[<CustomEquality; CustomComparison>]
type FunkyUnion =
    | Case1
    | Case2
    | Funky of (unit -> unit)

    override x.Equals(obj) =
        //This here will cause a stackoverflow to occur...
        match obj with
        |  :? FunkyUnion as y -> (x = y)
        |  _ -> false

    override x.GetHashCode() =
        match x with
        | Case1 -> 1
        | Case2 -> 2
        | _     -> 3

    interface System.IComparable with
       member x.CompareTo yobj = 
          match yobj with 
          | :? FunkyUnion as y -> compare x y
          | _ -> invalidArg "yobj" "cannot compare value of different types"

    interface System.IComparable<FunkyUnion> with
        member x.CompareTo(y) =
            compare x y

[<EntryPoint>]
let main argv = 

    let funky1 = FunkyUnion.Case1
    let funky2 = FunkyUnion.Case2

    if funky1 = funky2 then
        printfn "!!!"

    printfn "!"

    0

1 个答案:

答案 0 :(得分:3)

您在等号中获得了堆栈溢出,因为您正在使用=与您正在实施Equals的同一类型。因此Equals调用==调用Equals,依此类推......

您想为Equals做些什么:

override this.Equals(o) =
    match o with
    | :? FunkyUnion as fu ->
        match this, fu with
        | Case1, Case1 | Case2, Case2 -> true
        | Funky f1, Funky f2 -> (* whatever you want to do for this case *)
        | _, _ -> false
    | _ -> false

您可以以类似的方式更新CompareTo。这应该让你开始。