[英]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.