Реализуйте равенство вне определения типа - PullRequest
2 голосов
/ 11 марта 2011

У меня есть несколько типов, которые реализуют интерфейс.Равенство для этих типов зависит только от членов интерфейса.Можно ли определить равенство для этих типов один раз, не переопределяя Equals или op_Equality для каждого типа?

EDIT

Я попробовал следующее, но,по любой причине он отменял каждое использование =, даже для типов, не реализующих IEntity.

[<AutoOpen>]
module Equality =
    let inline op_Equality (left:IEntity) (right:IEntity) = true

Я также пытался использовать гибкие типы (#IEntity).Тот же результат.

Ответы [ 2 ]

2 голосов
/ 11 марта 2011

То, что вы пытаетесь сделать, - это то, что миксины или классы типов могут включить на других языках; к сожалению, в F # нет эквивалентной функциональности. Ваш лучший выбор, вероятно, один из следующих вариантов:

  1. Используйте абстрактный базовый класс вместо интерфейса.
  2. Напишите ваш метод равенства за пределами вашего типа, а затем перенесите все свои реализации на него. Например,

    let entityEquals (i1:IEntity) (i2:IEntity) =
      i1.Member1 = i2.Member1 &&
      i1.Member2 = i2.Member2 &&
      ...
    
    type MyEntity() =
      interface IEntity with
        member x.Member1 = ...
        ...
        override x.Equals(y) = 
          match y with
          | :? IEntity as y -> entityEquals x y
          | _ -> false
        override x.GetHashCode() =
          ...
    

    В дополнение к небольшому шаблону, минусом здесь является то, что если кто-то еще реализует ваш IEntity интерфейс, он не будет вынужден использовать ваш метод равенства - он согласен.

  3. Создайте еще один оператор, который вы используете для проверки равенства IEntity s:

    let (==) (i1:IEntity) (i2:IEntity) =
      i1.Member1 = i2.Member1 &&
      ...
    

    (Огромный) недостаток этого заключается в том, что структурное равенство типов, содержащих IEntity s (таких как кортежи, записи и т. Д.), Не будет использовать этот оператор для сравнения этих компонентов, что может привести к неожиданному нарушению код.

1 голос
/ 11 марта 2011

Я не думаю, что есть способ сделать это статическим способом. Проблема заключается в том, что члены расширения (например, если вы добавили op_Equality в качестве расширения) игнорируются статическими ограничениями членов (например, если вы также переопределили =, используя inlin с op_Equality требование).

Компилятор F # обладает некоторыми специальными возможностями, доступными только при компиляции FSharp.Core.dll, которые могут помочь (поиск источников для объявления let inline GenericOne). Он использует что-то вроде статического переключателя типа - но простые смертные не могут получить к нему доступ.

Итак, у меня нет никакой идеи лучше, чем использовать динамическое тестирование типов, что на самом деле не очень хороший подход, и, вероятно, лучше определить пользовательский оператор для сравнения ваших интерфейсов.

Для справки, уродливый динамический подход будет:

let inline (=) a b =
  match a, b with
  | :? IFoo as a, :? IFoo as b -> yourEquals a b
  | _ -> a = b
...