繁体   English   中英

如何在F#中的抽象(接口)类型上集中定义IComparable

[英]How to centrally define IComparable on abstract (interface) types in F#

这个问题有点类似于使用自定义类F#Set的下一个级别-我想为通用接口定义IComparable。

我有一组任意类型,它们实现了共享的元数据交换接口ITree 我只想使用ITree公开的数据来比较这些类型。

我意识到这些东西并非完全是惯用的F#,但是我正在尝试与现有的C#和VB代码互操作,因此我想尽可能使用.NET接口和比较。

open System
open System.Collections.Generic

// Simplified "generic" metadata type that all implementers agree on
type ITree =
  abstract Path: string with get, set
  abstract ModifyDate: DateTime with get, set

type Thing1(path, date) =
  interface ITree with
    member x.Path = path
    member x.ModifyDate = date

// In reality, the types implementing ITree are going to
// come from different external assemblies
type Thing2(path, date) =
  interface ITree with
    member x.Path = path
    member x.ModifyDate = date

let d1 = DateTime.Now
let d2 = DateTime.Now.AddMinutes(-2.0)
let xs : seq<ITree> = Seq.cast [ Thing1("/stuff", d1); Thing1("/dupe", d1); Thing1("/dupe", d1) ]
let ys : seq<ITree> = Seq.cast [ Thing2("/stuff", d2); Thing2("/dupe", d1) ]

// Then I would like to take advantage of F# Sets
// to do comparison across these things
let xset = Set.ofSeq xs
let yset = Set.ofSeq ys

let same = Set.intersect xset yset
let diffs = (xset + yset) - same

现在是实际的问题:由于ITree尚未实现IComparable因此无法编译。 我需要进行自定义比较,以帮助解决时钟偏斜以及其他问题。

有没有一种方法可以直接在ITree定义比较函数以便所有其他程序集都无需考虑它,而只需提供其数据即可?

如果我尝试去做

type ITree =
  abstract Path: string with get, set
  abstract ModifyDate: DateTime with get, set
    interface IComparable<ITree> with
      let Subtract (this: ITree) (that: ITree) =
        this.ModifyDate.Subtract(that.ModifyDate)
      match compare (this.Path, this.ParentPath) (that.Path, this.ParentPath) with
      | 0 ->
        // Paths are identical, so now for a stupid timespan comparison
        match abs (Subtract this that).TotalSeconds with
        | x when x > 60.0  -> int x
        | _ -> 0
      | x -> x

编译器认为ITree不再是抽象接口或令人困惑的东西。

现在,我可以创建所有实现者都必须共享的基本类型,但是我不想这样做,因为那些其他类型实际上只需要在此接口上公开其数据,它们已经存在,并且可能已经有一个基本类型。由于其他原因而上课。

可能我可以使用IComparer<T>

type ITreeComparer =      
  interface IComparer<ITree> with
    member x.Compare(this, that) = ...

但是后来我不知道如何告诉Set...函数使用该IComparer。

(我假设一旦确定了如何应用IComparer<T> ,相同的方法将根据需要用于IEqualityComparer<T> 。)


编辑:我可以

let x = new HashSet<ITree>(a |> Seq.cast<ITree>, new ITreeEqualityComparer())

要使用普通的.NET集合,该集合足以解决此问题; 但是,我仍然想知道是否有更好的方法来做我想做的事情。

如果要将数据存储在F#集合中(集合不适用于自定义IComparers),则需要创建包装器类型。 所以你可以做例如

type TreeWithComparer(tree:ITree) =
    member this.Data = tree
    interface IComparable with ...
        // define the custom logic you need

然后将这些包装对象存储在Set中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM