Как переопределить GetHashCode и CompareTo для дискриминируемого союза в F #?

У меня есть простое размеченное объединение F #, которое объединяет логическое значение, строку и число с плавающей запятой. Я хочу переопределить Object.Equals (arg) этого объединения, чтобы я мог добавить эпсилон для учета ошибок точности при проверке равенства с плавающей запятой. Компилятор жаловался, что если я переопределю this.Equals (arg), я также должен переопределить this.GetHashCode () и this.CompareTo (arg). Для их переопределения я не планирую никаких специальных функций, поэтому я просто хотел бы вызвать версии этих методов по умолчанию. В моей реализации сейчас у меня есть три вызова GetHashCode и три вызова CompareTo для каждого типа в моем размеченном объединении: по одному для каждого типа.

Есть ли способ закодировать переопределение GetHashCode с помощью всего одного вызова GetHashCode? Тот же вопрос для CompareTo? Все типы в моем размеченном объединении реализуют ICompareable.

[<CustomEquality;CustomComparison>]
type MyType =
    | Bool of bool
    | Str of string
    | Float of float

    override this.Equals(arg) =
        let epsilon = 0.1
        match arg with
        | :? MyType as other ->
            match this, other with
            | Bool(a), Bool(b) -> a = b
            | Str(a), Str(b) -> a = b
            | Float(a), Float(b) -> Math.Abs(a - b) < epsilon
            | _ -> false
        | _ -> false

    override this.GetHashCode() =
        match this with
        // Three calls to GetHashCode.  I'd like only one
        | Bool(a) -> a.GetHashCode()
        | Str(a) -> a.GetHashCode()
        | Float(a) -> a.GetHashCode()
        | _ -> 0

    interface System.IComparable with
        member this.CompareTo arg =
            match arg with
                | :? MyType as other ->
                    match this, other with
                    // Three calls to CompareTo.  I'd like only one
                    | Bool(a), Bool(b) -> a.CompareTo(b)
                    | Str(a), Str(b) -> a.CompareTo(b)
                    | Float(a), Float(b) -> a.CompareTo(b)
                    | _ -> 0
                | _ -> 0

person user2023861    schedule 08.12.2014    source источник
comment
Это плохая идея ... Причина, по которой компилятор настаивает на переопределении GetHashCode и CompareTo, заключается в том, что они должны быть совместимы с равенством (одинаковые объекты должны иметь одинаковые хэш-коды и должны сравниваться).   -  person kvb    schedule 09.12.2014
comment
В вашем типе кажется, что два значения могут сравниваться равными, но не иметь одинаковый хэш-код? Для меня это звучит как ошибка. Лично я бы создал отдельную функцию occEqualTo или аналогичную и не путал бы ее с базовой моделью равенства .NET.   -  person Grundoon    schedule 09.12.2014


Ответы (1)


Вы можете определить вспомогательное свойство, которое извлекает содержимое DU как obj:

member this.Value = 
  match this with
  | Bool(b) -> box b
  | Str(s) -> box s
  | Float(f) -> box f

Затем вы можете реализовать GetHashCode, просто получив значение (что включает в себя некоторый бокс, поэтому он будет немного медленнее) и вызов GetHashCode для возвращенного объекта:

override this.GetHashCode() =
  this.Value.GetHashCode()
person Tomas Petricek    schedule 08.12.2014
comment
Есть ли способ использовать это с CompareTo? - person user2023861; 09.12.2014
comment
Я забыл отметить вас в своем комментарии. - person user2023861; 09.12.2014
comment
@ user2023861: замените box приведением к IComparable. - person Daniel; 09.12.2014
comment
@Daniel, я пробовал это (| Bool(b) -> System.IComparable b) и получаю сообщение об ошибке недопустимое использование типа интерфейса - person user2023861; 09.12.2014
comment
Синтаксис преобразования вверх - b :> IComparable. - person Daniel; 09.12.2014
comment
Это отвечает на поставленный вопрос, но не рекомендуется; см. мой комментарий к вопросу. - person kvb; 09.12.2014