为什么一个在类中实现的C#接口方法必须是公共的?

32

我有一个继承了接口的类。在我的类中,实现了一个接口成员方法,但没有设置访问修饰符(因此默认为私有)。

我得到了一个错误提示:"无法实现接口成员,因为它不是公共的"。

为什么不允许这样做?我不能覆盖访问权限吗?

3个回答

45

以下是一个例子,解释了为什么允许重写可见性是毫无意义的:

interface someI
{
    void doYourWork();
}
public class A : someI
{
    public void doYourWork()
    {
        //...
    }
}

public class B : someI
{
    private void doYourWork()
    {
        //...
    }
}
void Main()
{
    List<someI> workers = getWorkers();
    foreach(var worker in workers)
        worker.doYourWork();
}
当你的工作者(worker)是B类型时,发生了什么?你调用了一个像公共方法一样的方法,但它是一个私有方法。如果你想要这个功能,那么它实际上不是一个私有方法,对吧?
如果你只想在通过你的接口引用时将其作为公共方法,那么你可以这样定义它:
public class B : someI
{
    void someI.doYourWork()
    {
        //...
    }
}

最终你得到了这个结果:

var b = new B();
b.doYourWork(); // Not accessible
((someI)b).doYourWork(); // Accessible

3
我建议对于未密封的类,如果接口实现不会公开相同名称和签名的公共类成员,则应使用受保护的虚成员进行实现;不幸的是,在C#中最接近实现此目的的方法是让接口实现仅调用受保护的虚方法。否则,如果派生类重新实现接口,将无法调用父级实现。 - supercat
1
@supercat 或许现在还太早,但我不确定我是否理解你的意思;你所说的“否则,如果派生类重新实现一个接口,就没有办法调用父类的实现”是什么意思?为什么要有一个实现接口的受保护方法(在这种情况下,您将无法通过接口访问它;只能在派生类中访问,这些派生类甚至可能不知道它们正在实现哪些接口)? - Rob
2
在vb.net中,可以这样说:Protected Overridable Sub DoSomethingImpl() Implements IDoSomething.DoSomething; 外部代码只能通过接口调用该方法,但派生类将能够重写该方法,并在重写中调用父方法。 在C#中,同样使用受保护的方法来实现接口会很有用,但是负责规范的人不允许该选项。 最接近的方法是使用一个显式实现接口的方法,该方法将调用受保护的方法 DoSomethingImpl()。 请注意... - supercat
1
实现方法的名称可以是 DoSomething() 而不是 DoSomethingImpl(),但使用前者的名称将防止派生类公开一个名为 DoSomething() 的方法。 - supercat

23

方法必须实现为 public,因为它们必须通过接口可调用,因此从接口作为类型可访问的地方调用。

这里有几个选项可以“更改”该方法的可见性。假设:

public interface IFoo 
{
    bool IsFoo();
}

A. 实现方法 明确

public class Foo : IFoo
{
    bool IFoo.IsFoo() { return true; }
}

该方法仅可通过接口(在本例中为IFoo)使用。

B. 更改接口的可见性

将接口定义为internal而不是public。然而,作为后果,Foo也必须是internal


在其他编程语言(如vb.net)中,实现接口的方法不需要特定的可访问性。C#要求隐式接口实现必须是公共的,因为没有其他关键字或符号来指示命名类成员也应被视为接口实现。 - supercat

6
要求接口实现为公共的是一个逻辑上的要求。当你实现一个接口时,你告诉编译器“嘿,我实现了这个接口中的每个方法”。因此,将方法设为私有就使其不再可访问 - 逻辑上也就无法实现。接口作为使用你对象代码的合同,表示你可以在我的对象上调用接口中定义的任何方法。如果实现方法被设为私有,则这一点将不再成立。
如果你想要隐藏你的实现(比如说对于 IntelliSense),那么你可以像 @Bryan 提到的那样显式地实现该方法。

1
接口作为一种合同,用于使用您的对象的代码,表示您始终可以在我的对象上调用接口中定义的任何方法。这并不完全正确。它意味着您可以将类的实例转换为接口,然后通过接口引用调用接口方法。这才是合同,而不是在没有首先转换为接口的情况下调用接口的方法的能力。当然,通常应该在不需要强制转换的情况下公开接口方法,但这既不是技术要求,也不总是逻辑要求。 - Darryl

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