为什么接口实现不能返回更具体的类型?

35

如果一个接口指定了一个属性或方法来返回另一个接口,为什么第一个接口的实现不允许将返回类型“改变”为更具体的类型?

我们举个例子来说明:

interface IFoo
{
    IBar GetBar();
}
interface IBar
{ }

class Foo : IFoo
{
    // This is illegal, we are not implementing IFoo properly
    public Bar GetBar()
    {
        return new Bar();
    }
}

class Bar : IBar
{ }

我知道如何使它工作,那不是我的问题。

我可以:

  • GetFoo()的返回类型更改为IBar,或者
  • 显式实现接口并在IFoo.GetBar()方法中调用GetBar

我真正想问的是,为什么不允许上面的代码编译。是否存在任何情况,上述内容未满足由IFoo指定的契约。


3
有第三种方式,比前两种稍微方便一些。你可以使用一个抽象类来实现接口,然后在你的“具体”类中继承抽象类而非接口。然后在你的“具体”类中,你可以使用“new”关键字来隐藏基类的方法。这对我来说运作得还不错。 - BrainSlugs83
我觉得BrainSlugs83在这里的评论是一个答案,因为它提供了一个不错的解决方法。 - carlin.scott
3个回答

21
通常情况下,我会说这需要权衡一下支持这种功能所增加的复杂性和带来的好处。(所有功能都需要设计、文档编写、实现、测试,之后开发人员还需要学习相关内容。)请注意,如果您想要支持返回实现了接口的值类型,那么可能会有一些重大的复杂性(因为它最终会得到不同的表示,而不仅仅是一个引用)。
在这种情况下,我不认为CLR甚至支持这样的特性,这将使C#难以干净利落地实现。
我同意这将是一个有用的功能,但我怀疑它还没有被认为足够有用以值得额外的工作。

8
Jon,我有一个问题。你如何在一分钟内写这么多东西? - Nikhil Agrawal
+1 是因为“...增加的复杂性”,但我不确定它是否有用。毕竟,接口是一种契约,如果你需要这样的方法,你可以显式地实现接口方法并添加另一个具有 required 返回类型的方法,那么这是为什么呢? - Adriano Repetti
1
@Adriano:是的,你可以这样做。但是必须这样做很烦人,而且显式接口实现有其自身的缺点和复杂性。我尽可能避免使用显式接口实现,如果支持协变返回类型,那么我就可以避免使用它了 :) - Jon Skeet
Jon,我不讨厌显式实现......但我想我看不出它的缺点!:| - Adriano Repetti
2
@Adriano:考虑一个从另一个使用显式接口实现的类派生的具体类。如果您想覆盖该接口实现,但仍要使用原始实现,则会遇到问题-您无法调用base.Foo(),但将其转换为接口最终会递归回到您的实现。它也不与“dynamic”兼容。 - Jon Skeet
显示剩余7条评论

8
你所询问的功能叫做“返回类型协变”。正如维基百科中所述,Java和C++都有这个功能,这可能使得C#没有这个功能感到惊讶。
Eric Lippert在对这个答案的评论中确认,这个功能没有被实现是因为它被认为不值得实现。 (该答案的先前版本将此决定的责任归因于Eric本人;他说这是不正确的,如果有任何一个人负责,那就是Anders Hejlsberg。)

无论如何,现在有各种提案将其添加到语言中(请参见https://github.com/dotnet/roslyn/issues/357https://github.com/dotnet/csharplang/blob/master/proposals/covariant-returns.mdhttps://github.com/kingces95/coreclr/issues/2),因此也许它将在未来几年内得以实现。根据这些讨论,从原则上讲,在C#中不存在这个功能的深刻原因 - 而是迄今为止没有被认为值得任何人付出努力去实现。


4
在我加入C#设计团队之前,Java已经添加了这个功能,所以当时并没有考虑我的意见。而在我加入C#设计团队后,据我回忆,设计团队从未考虑过这个问题。因此更准确地说,是Anders认为这不值得花费精力。我并不认为这是一个糟糕的功能,如果C#具备这个功能,我会使用它。不过我同意,还有许多其他功能应该优先考虑。 - Eric Lippert

2

这个功能没有被实现,因为他们觉得这个特性不值得花费那么多的精力。但是有好消息。

这个功能已经在2020年3月Mads Torgersen发布的文章中宣布,将在即将推出的C# 9.0中实现。

abstract class Animal
{
    public abstract Food GetFood();
    ...
}
class Tiger : Animal
{
    public override Meat GetFood() => ...;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接