在不同的通用实例中实现相同的接口

时间:2009-09-23 05:27:00

标签: f# interface generics

在C#中,我可以使用两个不同的类型参数在一个类上实现两次泛型接口:

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#中不允许这样做。

我在网上找不到任何discussion of this issue。这种用法是不是因为某些原因而不受欢迎?是否有计划在即将发布的F#版本中允许这样做?

2 个答案:

答案 0 :(得分:11)

现在我不知道允许这样做的计划。。功能has been planned,至少部分(见注释)在F#4.0中实现。

我认为目前不允许的唯一原因是它实施起来非常重要(特别是使用F#类型推断),而且很少在实践中出现(我只记得有一位客户问过这个问题)。

考虑到无限的时间和资源,我认为这是允许的(我可以想象这会被添加到该语言的未来版本中),但是现在看来这不是一个值得努力的功能支持。 (如果您知道强烈的激励案例,请发送邮件至fsbugs@microsoft.com。)

修改

作为好奇的实验,我写了这个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"); }
}

并从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#也可以使用这个东西。

答案 1 :(得分:1)

有一个合理但不优雅的方法,为每个接口创建一个新类型这里是一个消耗来自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)