Подтвердить что ты не робот

Реализация одного и того же интерфейса при разных генерирующих экземплярах

В С# я могу реализовать общий интерфейс дважды в одном классе, используя два разных типа-параметра:

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 #?

4b9b3361

Ответ 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)