为什么接口只有公共成员和方法

3
这是一道面试中非常基础的问题,我们能否在接口内声明私有方法。我的回答很简单,在接口中我们只能拥有公共变量或方法。那么下一个问题来了...为什么呢?

6
如果这是私有的,你会如何实现接口? - Sayse
1
成员和方法不能被保护/打包吗?私有意味着它们无法被实现,所以这可能是为什么它不合逻辑的原因,但是关于受保护和打包,这是有意义的。 - Hybris95
可能是为什么我们应该将接口方法声明为公共的?的重复问题。 - Dimitris Kalaitzis
2
@Hybris95:强制实施受保护的接口有什么价值? - Oliver Charlesworth
2
@DimitrisKalaitzis请记住我们在谈论C#规范而不是Java。 - Hybris95
显示剩余4条评论
4个回答

2
接口具有公共契约的语义,由某些实现提供。就像类(或全局模块、子系统、组件等)向其客户端提供的API一样。
私有方法用于处理某些内部逻辑,因此取决于实际实现。它是上下文相关的。所以,如果类客户端需要调用私有方法,那么1)它必须是公共的,或者2)存在不良的设计实践。
这就是为什么作为抽象的接口可以被认为是面向对象编程(OOP)的基础,它是实现抽象、封装和多态等事物的非常重要的工具。

2
有关接口成员如何具有不同可见性的解释有很多种。虽然可能存在更多的标识符可见性级别,但让我们仅看一下在C#中可用的级别,并思考它们是否可以有意义地集成到接口中。
1)接口成员仅根据各自可见性级别的规则对接口外的代码可见。
  • public: 在C#中,接口成员默认为公共的,因此这个可以工作。
  • internal: 如果单个接口成员可以声明为internal,那么这意味着接口的一部分只能由该接口所在程序集中的类来实现。当抽象类成员或抽象类的构造函数被声明为internal时,已经存在类似的情况 - 这有效地防止了基类程序集之外的子类化。
    至于可能使用这种情况的场景,请考虑返回对象的API,后者再次被该API使用的情况。如果由于某种原因,API只能使用它自己实例化的对象,则能够以一种使第三方代码无法派生其自定义类,但仅使用从API获得的内容的方式声明基类,而不能做到与接口相同的事情,感觉奇怪且不对称。
  • protected: 受保护的成员仅从其自身类及其任何子类中可见。应用上述假设,这意味着只有接口声明本身内的代码(接口几乎不能包含与此相关的代码1),派生的接口(与前面相同)和可能实现接口的类才能看到这些受保护的成员。
    后一种情况可能会变得有趣,即使在相当牵强的情况下:该类内的方法可能希望消耗任何实现接口的东西,无论是该类本身的当前实例还是其中嵌套的任何其他类型,同时获得半独占访问权限受保护的方法。
  • private: 所有具有private可见性的内容仅对声明接口本身可见。由于接口成员几乎不能从接口本身引用1,因此这可能不是非常有用。但是,仍然可能存在一些角落情况,例如在接口中定义的const定义,在该接口中的属性中引用,但不应对外部代码可用 - 但是,C#不允许在接口中使用const,即使CLI世界中也没有类似的功能
2) 接口成员必须使用接口中指定的(至少?)可见性进行实现,并且在通过类类型引用访问成员时遵守该可见性,但是当通过接口类型引用访问成员时通常是公开可见的。对于公共成员,不会有任何更改,而内部、受保护和私有可见性则允许创建似乎是部分实现的接口。类似的效果目前可以通过显式接口实现来实现,因此在某种程度上,这将是语法糖,以避免重复编写相同的方法,一次具有所需的可见性,一次具有特定于接口的显式实现声明(即使后者通常只转发调用)。
3) 为了完整起见,即使您的问题似乎没有包括这个选项:对于每个类,整个接口的可见性由实现者确定。
这样,一个类可以实现一个接口,但该事实只在相应的可见性级别内显现,而在外部,该类将不被认为是可赋值或可转换为该接口。
如果接口本身需要比类更低的可见性,则这可能特别有益,因为接口应仅在内部处理,并且对外部世界毫无意义。目前,在C#中不可能做到这一点;接口需要至少与其实现类一样可访问。但是,这也可以通过在公共类中实现一个内部接口来实现。
由于界面成员的可见性有很多种方式是可行的,至少在某些情况下是有用的,所以对于这个问题最明确的答案可能是“因为开发团队决定以这种方式指定C#”。 可以假设,在同一界面的属性声明中,C# 6的nameof运算符可以用于受保护/私有界面成员。

1

接口是类所需实现的合同,以确保某个给定类的使用者将接收必须实现接口的类的实例。

如果接口允许私有成员,那么无法调用公共表面隐藏的接口成员的事实将破坏接口的目的,因为使用者将无法调用这些私有成员(那么它为什么要使用接口呢?)。

例如,接口为对象提供类型。如果某些使用者代码依赖于接收实现某个接口的对象,以避免对使用它的程序集和整个程序集产生冗余依赖关系,但整个程序集都可以访问不公开提供成员的接口,那么该使用者如何使用所谓的对象?

public interface IDoesSomething
{
     private void Do();
} 

public void SomeMethod(IDoesSomething some) 
{
    some.????? // <---- what? the object doesn't have public members!
}

事实上,由于接口只是元数据,我理解你可能会认为“我会声明私有成员以强制实现者按预期顺序调用某些逻辑”,但是再次提醒,由于它们只是元数据,你无法确保某个公共成员将按正确顺序调用私有成员。而且实现者无法访问接口的私有成员,因为它们需要受到保护(这就是为什么抽象类存在的原因)...

2
将内部接口的成员实现为内部方法/属性可能是合乎逻辑的。遗憾的是,这不是语言设计的方式。 - Ndech
@MatíasFidemraizer: 我能想象出同时具有公共和内部成员的接口。这些内部成员对其他程序集是不可见的(因此该接口就不能在其他程序集中实现,类似于当你给抽象类只提供内部构造函数时)。这可能是一种优雅的限制方法,可以将方法参数类型限制为由声明该接口的程序集声明的类的接口类型。它们与类中的内部成员类似地工作。 - O. R. Mapper
@MatíasFidemraizer:你可能也会使用接口,因为你自己的API在底层可以返回各种不同的内部类。而你的API的用户可能需要将这些实例传回到API的其他部分。这些部分期望你自己的某个类的实例,而不是其他类型的实例——也许是为了确保某些不变量,比如可序列化性,这不能仅通过类型限制来强制执行(?)通常,这样的情况可以通过具有内部成员的类来解决,但是,无法使用接口进行相同操作感觉不对称。 - O. R. Mapper
@MatíasFidemraizer:这种使用情况的重点在于,不允许第三方库实现接口,它们只能使用由您的 API 提供的实现接口的实例,并将这些实例传递回您的 API。与具有内部构造函数的抽象类相比,第三方库也不允许从中派生新类,它们只能使用实现程序集提供的实例。 - O. R. Mapper
1
@MatíasFidemraizer:这个问题问的是为什么C#不允许除了public以外的任何接口成员可见性。当你阅读这个问题的所有答案和评论时,你会看到各种用户写了一些类似于“C#中的接口成员只能是public,因为这是唯一有意义的方式。”我只是通过展示内部接口成员如何有用来解释我不同意这种说法的原因。这个问题不是在问当前C#实现的内容是什么或不是什么,它特别地在问原因。 - O. R. Mapper
显示剩余10条评论

-2

答案太简单了;

当一个类继承自一个接口时,该接口的成员和方法应该可以被该类访问,这就是为什么它们必须是公共的。


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