2009-11-23 28 views
1

此問題屬於F# Set using custom class的下一級 - 我想爲通用接口定義IComparable。如何在F#中抽象(接口)類型中集中定義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集合,對於這個問題應該足夠好;但是,我仍然想知道是否有更好的方法來做我想做的事情。

回答

3

如果要將數據存儲在F#集中(Set不是爲了與自定義IComparers一起工作),您需要創建一個包裝類型。所以你可以做

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

然後將這些包裝器對象存儲在Set中。

+1

不錯,那是我需要做的。我剛開始嘗試使用HashSet 和man,簽名是可怕的... UnionWith(seq ):unit - auuuugh!就地修改! – 2009-11-23 17:28:23

+0

這很好。沒有難看的代碼重複。現在我只希望我足夠聰明,想到這一切都在我寂寞的... – 2009-11-23 18:59:00

相關問題