【问题标题】:Implementing the same interface at different generic instantiations在不同的泛型实例中实现相同的接口
【发布时间】:2009-09-23 05:27:00
【问题描述】:

在 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&lt;float&gt;''IFoo&lt;int&gt;' 处实现或继承相同的接口。此版本的 F# 中不允许这样做。

我在网上找不到任何discussion of this issue。出于某种原因,这种用途是否令人不悦?是否有计划在即将发布的 F# 中允许这样做?

【问题讨论】:

标签: f# interface generics


【解决方案1】:

目前我不知道有计划允许这样做。。功能 has been planned 至少部分(参见 cmets)在 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# 中引用它(cmets 建议结果)

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# 可以正常使用这些内容,即使您无法从该语言中创作相同的内容。

【讨论】:

  • 呃,那么 F# 类型推断将如何处理用 C# 编写的此类类型呢?至于基本原理,嗯……ECMA CLI 规范定义了各种类别的 CLS 合规性;其中之一是“CLS 扩展器”。对此的一项要求是:“能够……实现任何符合 CLS 的接口。”
  • 扩展 Pavel 的评论,如果你在 C# 中定义一个非泛型接口 I 并同时扩展 IG&lt;int&gt;IG&lt;string&gt;,会发生什么?这个接口可以在 F# 中实现吗?
  • @kvb,不,该接口无法从 F# 实现。
  • 这是处理面向对象风格的事件处理系统的方式,例如为几种不同类型的事件实现 IHandle。可能还有其他更实用的方法,但这样做有一些话要说。
  • 这是一个案例:我正在实现一个 IDictionary,它也需要作为 ICollection 进行迭代,因为它实现了 NotifyCollectionChanged。似乎是一个简单的场景。
【解决方案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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-25
    相关资源
    最近更新 更多