I'm trying to use Set operations with a class that I have. Every instance of this class has a unique ID. Do I need to implement the System.IComparable interface and if so how would I?
type SomeClass(id : int) =
member this.ID = id
let someSet = Set.of_list [SomeClass(1); SomeClass(2)]
let test = someSet.Contains(SomeClass(2))
Here's an implementation that should work:
type SomeClass(id : int) =
member this.ID = id
override this.Equals(o) =
match o with
| :? SomeClass as sc -> this.ID = sc.ID
| _ -> false
override this.GetHashCode() =
id.GetHashCode()
interface System.IComparable with
member this.CompareTo(o) =
match o with
| :? SomeClass as sc -> compare this.ID sc.ID
| _ -> -1
I believe you will need to implement IComparer<T>
for set comprehensions (e.g. Set.of_list
) to work. (Not IComparable<T>
, which tends to be less widely used - though I may be wrong.)
This blog post explains in general how to implement interfaces in F#. It also includes a specific example of a type implementing the IComparer<T>
, which actually isn't straightforward as you might hope.
type Comp() =
interface IComparer with
member x.Compare(a, b) = 0
member x.Compare(a, b) = (x :> IComparer).Compare(a,b)
Let me know if the works for you. I have some suspicion that you might in fact need to implement the IEqualityComparer<T>
instead, since that's what LINQ set extension methods are based around, as far as I know. (It really gets confusing with all these interfaces for comparing in the BCL!)
Regarding the comment on my other answer, you could factor this into a reusable base class, but I'm not sure it's really a good idea:
type EqCompBase<'EqKey,
'DerivedType when 'DerivedType :> EqCompBase<'EqKey,'DerivedType> >
(id : 'EqKey) =
member this.ID = id
override this.Equals(o) =
match o with
| :? EqCompBase<'EqKey, 'DerivedType> as sc -> this.ID = sc.ID
| _ -> false
override this.GetHashCode() =
id.GetHashCode()
interface System.IComparable with
member this.CompareTo(o) =
match o with
| :? EqCompBase<'EqKey, 'DerivedType> as sc -> compare this.ID sc.ID
| _ -> -1
type SomeClass(id : int, otherFieldThatDoesNotMatterForEquality : string) =
inherit EqCompBase<int, SomeClass>(id)
let someSet = Set.of_list [SomeClass(1,"yadda"); SomeClass(2,"blah")]
let test = someSet.Contains(SomeClass(2,"foo"))
printfn "%A" test // true