在C#中,为什么接口实现必须显式地实现另一个版本的方法?

13

拿这个例子来说:

public interface IFoo
{
    IFoo Bar();
}

public class Foo : IFoo
{
    public Foo Bar()
    {
        //...
    }

    IFoo IFoo.Bar() { return Bar(); } //Why is this necessary?
}

为什么即使 Foo 可以不需要强制转换就能转换为 IFooIFoo Bar() 的隐式实现还是必要的?


你为什么要这样做呢?这实际上将Foo的用户绑定到具体实现,使得单元测试中的模拟变得困难,并且通常会增加应用程序的耦合度... - Steve Czetty
这两种方法基本上是互斥的。两者都可行,但目的不同。您不会在同一具体类中的相同界面中使用这两种方法(至少我认为不会)。 - dkackman
@SteveCzetty 因此,原帖中的问题是:何时需要显式实现接口。 - Aaron McIver
我的观点是,任何引用 Foo 具体版本(而不是 IFoo)的类都将与该特定实现耦合。 - Steve Czetty
可能是隐式 vs 显式接口实现的重复问题。 - David Basarab
5个回答

5

微软在该主题上有一篇详细的写作,但它归结为实现具有相同方法的多个接口/类。隐式方法在这种情况下不再有效

class Test 
{
    static void Main()
    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method.  
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

// Output: 
// Paint method in SampleClass 
// Paint method in SampleClass 
// Paint method in SampleClass

如果我们采用显式方法,就会得到如下结果。
public class SampleClass : IControl, ISurface
{
    void IControl.Paint()
    {
        System.Console.WriteLine("IControl.Paint");
    }
    void ISurface.Paint()
    {
        System.Console.WriteLine("ISurface.Paint");
    }
}

这一切都归结于在实现类型冲突时提供独特性。在你的例子中,Foo 就是 IFoo

这里只有一个接口。 - Matt
这并没有回答关于返回类型的问题。 - Ilya Kogan
@Matt 稍微调整措辞,以指出单个接口在使用,但该类也实现了该方法签名。 - Aaron McIver
@IlyaKogan 在 Foo 周围做了一个关于 IFoo 的注释。 - Aaron McIver

5
在这种情况下需要使用它,因为C#不支持接口的返回类型协变,所以您的函数需要这个。
public Foo Bar()
{
    //...
}

由于Bar方法的返回类型不同,所以它不能满足IFoo接口的要求。

既然您希望实现该接口,那么您唯一的选择是显式地这样做,因为您已经在类上定义了一个Bar()方法。


是的,但是为什么C#不允许这样做呢? - Ilya Kogan
2
@IlyaKogan - 我不知道为什么C#不支持返回类型协变-你得问设计者。C#通常更喜欢事情明确,所以这可能是为了防止接口被意外地隐式实现。 - Lee
1
@IlyaKogan 这归结于关于实现方面的每个“为什么”问题;因为他们选择了这样做。 - Aaron McIver
@AaronMcIver 通常,一个实现选择可以通过逻辑方式来解释。 - Ilya Kogan
很多时候,只是因为他们认为有更重要的事情需要花时间去做。 - Erix

4
您可以这样解决它(有点丑,但照顾到强类型):
public interface IFoo<T> where T : IFoo<T>
{
    T Bar();
}

public class Foo : IFoo<Foo>
{
    public Foo Bar()
    {
        //...
    }
}

1
非常感谢! - Matt
由于没有非泛型的 IFoo 接口,因此这段代码无法编译。 - D Stanley

3

因为您可能并不总是希望实现接口的方法与具有相同签名的另一个版本的方法表现出相同的行为。

您可能还希望一个类实现接口的方法,但该方法不可从类的实例本身访问。


0
我建议您将隐式实现保持为受保护状态,而不是公共状态。
public class Foo : IFoo
{
    **protected virtual** Foo Bar()
    {
        //...
    }

    IFoo IFoo.Bar() { return Bar(); } 
}

这个主题中有一个相当详细的回答,解释何时以及为什么使用显式实现:

隐式与显式接口实现

使用显式实现的好处之一是,在使用Foo类时可以轻松地使用依赖注入来实现更松散的耦合。


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