Этот вопрос является видом следующего уровня F # Установить с использованием пользовательского класса - Я хочу определить IComparable для общего интерфейса.
У меня есть произвольный набор типов, которые реализуют общий интерфейс обмена метаданными, ITree
. Я хочу сравнить эти типы, используя только открытые данные в ITree
.
Я понимаю, что этот материал не совсем идиоматический F #, но я пытаюсь взаимодействовать с существующими С# и 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-коллекции, которые должны быть достаточно хороши для этой проблемы; однако, я все равно хотел бы знать, есть ли лучший способ сделать то, что я пытаюсь сделать.