F # Set с использованием настраиваемого класса

Я пытаюсь использовать операции Set с классом, который у меня есть. Каждый экземпляр этого класса имеет уникальный идентификатор. Нужно ли мне реализовать интерфейс System.IComparable, и если да, то как бы это сделать?

type SomeClass(id : int) =
    member this.ID = id

let someSet = Set.of_list [SomeClass(1); SomeClass(2)]
let test = someSet.Contains(SomeClass(2))    

person bhd739ge    schedule 21.05.2009    source источник


Ответы (3)


Вот реализация, которая должна работать:

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
person Brian    schedule 21.05.2009
comment
Потрясающе, спасибо, есть ли что-нибудь в пакете питания F #, реализующее шаблон активной записи? Было бы здорово, если бы я мог просто унаследовать это. Хм, может быть, я добавлю еще к этому классу и буду использовать его как таковой. - person bhd739ge; 22.05.2009
comment
Вы думали об использовании словаря вместо набора? - person gradbot; 22.05.2009
comment
@gradbot - вероятно, HashSet, а не словарь. Эти изменяемые типы коллекций .Net устраняют необходимость в IComparable, но по-прежнему нуждаются в Equals () и GetHashCode (). - person Brian; 22.05.2009

Я считаю, что вам нужно будет реализовать IComparer<T> для работы заданных представлений (например, Set.of_list). (Нет IComparable<T>, который, как правило, менее широко используется - хотя я могу ошибаться.)

В этом сообщении в блоге в целом объясняется, как реализовать интерфейсы. в F #. Он также включает конкретный пример типа, реализующего IComparer<T>, что на самом деле не так просто, как вы могли бы надеяться.

type Comp() =  
    interface IComparer with  
        member x.Compare(a, b) = 0  
    member x.Compare(a, b) = (x :> IComparer).Compare(a,b)  

Сообщите мне, работает ли это для вас. У меня есть подозрение, что вам на самом деле может потребоваться реализовать IEqualityComparer<T>, поскольку, насколько мне известно, на этом основаны методы расширения набора LINQ. (Это действительно сбивает с толку все эти интерфейсы для сравнения в BCL!)

person Noldorin    schedule 21.05.2009

Что касается комментария к другому моему ответу, вы можете включить его в многоразовый базовый класс, но я не уверен, что это действительно хорошая идея:

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
person Brian    schedule 22.05.2009