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