Implementing the same interface at different gener

2019-01-15 06:24发布

In C#, I can implement a generic interface twice on one class, using two different type-parameters:

interface IFoo<T> { void Foo(T x); }

class Bar : IFoo<int>, IFoo<float>
{
    public void Foo(int x) { }
    public void Foo(float y) { }
}

I would like to do the same thing in 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 = ()

But it gives a compiler error:

This type implements or inherits the same interface at different generic instantiations 'IFoo<float>' and 'IFoo<int>'. This is not permitted in this version of F#.

I can't find any discussion of this issue on the web. Is such use frowned upon for some reason? Are there plans to allow this in an upcoming release of F#?

2条回答
混吃等死
2楼-- · 2019-01-15 06:54

Right now I don't know of plans to allow this.. The feature has been planned and is, at least partially (see comments) implemented in F# 4.0.

I think the only reasons its currently disallowed are that it's non-trivial to implement (especially with F# type inference), and it rarely arises in practice (I only recall one customer ever asking about this).

Given an infinite amount of time and resources, I think this would be allowed (I can imagine this being added to a future version of the language), but right now it does not seem like this is a feature worth the effort of supporting. (If you know a strong motivating case, please mail fsbugs@microsoft.com.)

EDIT

As an experiment for the curious, I wrote this C#:

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"); }
}

and referenced it from F# (with comments suggesting the results)

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"

so this is what typically happens on this 'boundary' stuff with F# - F# can consume this stuff ok, even if you can't author the same stuff from within the language.

查看更多
祖国的老花朵
3楼-- · 2019-01-15 07:01

There is a reasonable although not elegant way to do it, create a new type for each interface here is an example of consuming multiple events from an ESB (nSvcBus) which requires that each event corresponds to an implemented interface. The first type below contains the generic 'handler' code, the other types just implement the interface and call to the generic handler

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)
查看更多
登录 后发表回答