Реализация одного и того же интерфейса при разных генерирующих экземплярах
В С# я могу реализовать общий интерфейс дважды в одном классе, используя два разных типа-параметра:
interface IFoo<T> { void Foo(T x); }
class Bar : IFoo<int>, IFoo<float>
{
public void Foo(int x) { }
public void Foo(float y) { }
}
Я хотел бы сделать то же самое в F #:
type IFoo<'a> = abstract member Foo : 'a -> unit
type Bar() =
interface IFoo<int> with
[<OverloadID("int")>]
member this.Foo x = ()
interface IFoo<float> with
[<OverloadID("float")>]
member this.Foo x = ()
Но это дает ошибку компилятора:
Этот тип реализует или наследует один и тот же интерфейс при разных генерируемых экземплярах 'IFoo<float>'
и 'IFoo<int>'
. Это недопустимо в этой версии F #.
Я не могу найти обсуждение этой проблемы в Интернете. По какой-то причине такое использование нахмурилось? Планируются ли это в следующем выпуске F #?
Ответы
Ответ 1
Прямо сейчас я не знаю о планах разрешить это.. Функция была запланирована и, по крайней мере, частично (см. Комментарии) реализована в F # 4.0.
Я думаю, что единственными причинами, которые в настоящее время не разрешены, является то, что он не является тривиальным для реализации (особенно с использованием вывода типа F #), и он редко возникает на практике (я вспоминаю только одного клиента, когда-либо спрашивающего об этом).
Учитывая бесконечное количество времени и ресурсов, я думаю, что это будет разрешено (я могу представить, что это добавлено в будущую версию языка), но сейчас это не похоже на то, что эта функция стоит усилий поддержка. (Если вы знаете сильный мотивирующий случай, напишите [email protected])
ИЗМЕНИТЬ
Как эксперимент для любопытных, я написал этот С#:
public interface IG<T>
{
void F(T x);
}
public class CIG : IG<int>, IG<string>
{
public void F(int x) { Console.WriteLine("int"); }
public void F(string x) { Console.WriteLine("str"); }
}
и ссылается на него с F # (с комментариями, предлагающими результаты)
let cig = new CIG()
let idunno = cig :> IG<_> // type IG<int>, guess just picks 'first' interface?
let ii = cig :> IG<int> // works
ii.F(42) // prints "int"
let is = cig :> IG<string> // works
is.F("foo") // prints "str"
так это то, что обычно происходит на этом "граничном" материале с F # -F #, может поглощать этот материал нормально, даже если вы не можете создавать одни и те же вещи из языка.
Ответ 2
Существует разумный, хотя и не элегантный способ сделать это, создать новый тип для каждого интерфейса здесь - пример потребления нескольких событий из ESB (nSvcBus), который требует, чтобы каждое событие соответствовало реализованному интерфейсу. Первый тип ниже содержит общий код обработчика, другие типы просто реализуют интерфейс и вызывают общий обработчик
type public nSvcBusEvents() =
member this.HandleEvents(msg:IEvent) = ()
//handle messages ie: let json = JsonConvert.SerializeObject(msg)
type public ActionLoggedHandler() =
interface IHandleMessages<Events.ActionLoggedEvent> with
member this.Handle(msg : ActionLoggedEvent) =
nSvcBusEvents().HandleEvents(msg)
type public ActionCompletedHandler() =
interface IHandleMessages<Events.ActionCompletedHandler> with
member this.Handle(msg : ActionCompletedHandler) =
nSvcBusEvents().HandleEvents(msg)
type public ActionFailedHandler() =
interface IHandleMessages<Events.ActionFailedHandler> with
member this.Handle(msg : ActionFailedHandler) =
nSvcBusEvents().HandleEvents(msg)