代码之家  ›  专栏  ›  技术社区  ›  Dan Fitch

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

  •  1
  • Dan Fitch  · 技术社区  · 16 年前

    这个问题是下一个层次的问题 F# Set using custom class --我想为泛型接口定义IComparable。

    我有一组实现共享元数据交换接口的任意类型, 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
    

    现在实际的问题是:这不会编译,因为 伊特雷 还没有实现 IComparable . 我需要一个自定义的比较,有助于时钟歪斜,最终其他事情。

    有没有办法定义比较函数 伊特雷 直接地

    如果我试着去做

    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
    

    编译器认为 伊特雷 不再是一个抽象的接口,或是令人困惑的东西。

    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集合,这应该足以解决此问题;然而,我仍然想知道是否有更好的方法来做我想做的事情。

    1 回复  |  直到 9 年前
        1
  •  3
  •   Brian    16 年前

    如果要将数据存储在F#集合中(集合不是为与自定义IComparers一起使用而设计的),则需要创建包装类型。所以你可以这样做。

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

    然后将这些包装器对象存储在集合中。