簡體   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