У меня была похожая проблема. Я также был ограничен использованием только стандартных библиотек и никаких внешних зависимостей. В то же время я хотел использовать существующие реализации интерфейсов IEqualityComparer
и IComparer
, которые у меня уже были под рукой.
Итак, в итоге я создал собственную структуру-обертку, которую теперь могу использовать со встроенными в F # Map и Set:
open System
[<Struct>]
[<CustomComparison>]
[<CustomEquality>]
type ComparisonAdapter<'T>(value: 'T, comparer: IComparer<'T>, eqComparer: IEqualityComparer<'T>) =
new(value) = ComparisonAdapter(value, Comparer<'T>.Default, EqualityComparer<'T>.Default)
member this.CompareTo (cmp: IComparer<'T>, v: 'T) = cmp.Compare(v, value)
member this.CompareTo (v: 'T) = this.CompareTo(comparer, v)
member this.CompareTo (c: ComparisonAdapter<'T>) = c.CompareTo(comparer, value)
member this.CompareTo (o: obj) =
match o with
| :? ComparisonAdapter<'T> as ca -> this.CompareTo(ca)
| :? 'T as t -> this.CompareTo(t)
| :? IComparable as cmp -> (-1)*(cmp.CompareTo(value))
| _ -> raise (NotSupportedException ())
member this.Equals (c: ComparisonAdapter<'T>): bool = c.Equals(eqComparer, value)
member this.Equals (cmp: IEqualityComparer<'T>, v: 'T): bool = cmp.Equals(v, value)
member this.Equals (v: 'T): bool = eqComparer.Equals(v, value)
override this.Equals (o: obj): bool =
if (o :? ComparisonAdapter<'T>) then this.Equals(downcast o: ComparisonAdapter<'T>)
else if (o :? 'T) then this.Equals(downcast o: 'T)
else false
override this.GetHashCode () = eqComparer.GetHashCode value
member this.Value with get () = value
interface IEquatable<'T> with member this.Equals other = this.Equals(eqComparer, other)
interface IComparable<'T> with member this.CompareTo other = this.CompareTo(comparer, other)
interface IComparable with member this.CompareTo o = this.CompareTo o
Я использую его для создания оболочки встроенной коллекции и внутреннего создания оболочки ключей / элементов, чтобы сделать код пользователя более удобным. Так что я бы хотел что-то вроде
type MyCustomMap<'TKey, 'TValue>(cmp: IComparer<'TKey>, eq: IEqualityComparer<'TKey>) =
let innerMap = new Map<ComparisonAdapter<'TKey>, 'TValue>()
member this Add key value =
let actualKey = new ComparisonAdapter<'TKey>(key, cmp, eq)
innerMap.Add actualKey value
// * other map manipulation members omitted for brevity *
Вы также можете использовать существующую карту и набор, но вам нужно создать адаптер для каждой клавиши, когда вы вызываете соответствующие Add
/ Remove
/ TryFind
и другие методы. Вместо этого я предпочитаю использовать оболочку коллекции, подобную приведенной выше, потому что это гарантирует, что каждый ключ будет иметь одинаковые реализации компаратора, а пользовательский код не сможет их изменить.