为什么我的接口成员不能受保护?


75

反对在接口上声明保护访问成员的论点是什么?例如,这是无效的:

public interface IOrange
{
    public OrangePeel Peel { get; }
    protected OrangePips Seeds { get; }
}

在此示例中,接口IOrange将保证实现者至少OrangePips为其继承者提供一个实例。如果实现者愿意,他们可以将范围扩大到完整public

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    public OrangePips Seeds { get { return new OrangePips(6); } }
}

protected成员在接口上的意图是为继承者(子类)提供支持合同,例如:

public class SpecialNavelOrange : NavelOrange
{
    ...
    // Having a seed value is useful to me.
    OrangePips seeds = this.Seeds; 
    ...
}

(不可否认,这不适用于struct

我看不出太大的情况下进行privateinternal在接口修饰符的,但是同时支持publicandprotected修饰符似乎是完全合理的。


我将尝试解释 protectedinterface通过将成员与interfaces完全分开s成员:

假设有一个新的C#关键字, support以强制执行继承者契约,以便我们声明如下:

public support IOrangeSupport
{
    OrangePips Seeds { get; }
}

这将使我们能够签订合同,以向其继承者提供受保护的成员:

public class NavelOrange : IOrange, IOrangeSupport
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    protected OrangePips Seeds { get { return null; } }
}

这不是特别有用,因为类首先会通过提供protected成员来暗示此合同。

但是我们也可以这样做:

public interface IOrange : IOrangeSupport
{
   ...
}

从而适用IOrangeSupport于所有实现IOrange并要求它们提供特定protected成员的类-我们目前无法做到这一点。



1
要充实这个问题,请考虑此用例。我有一个派生类,它是从通用基类继承的。我想添加一个受保护的成员,该成员可以在派生类的任何常规样式中访问,但不暴露于外界。 class Base<T> { } interface IDerived { string Secret { get; set; } } class Derived<T> : Base<T>, IDerived { protected string Secret; protected void LearnSecret(IDerived other) { var x = other.Secret; } }
hypehuman 2015年

我很惊讶没有答案或评论提到EIMI。通过实现类型的角度查看成员时,EIMI将接口成员设为私有。
qqqqqqq

可能会出现在C#8.0jeremybytes.blogspot.com
2019/

Answers:


61

我认为每个人都坚决反对只包含公共成员而没有实现细节的接口。您正在寻找的是一个抽象类

public interface IOrange
{
    OrangePeel Peel { get; }
}

public abstract class OrangeBase : IOrange
{
    protected OrangeBase() {}
    protected abstract OrangePips Seeds { get; }
    public abstract OrangePeel Peel { get; }
}

public class NavelOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return null; } }
}

public class ValenciaOrange : OrangeBase
{
    public override OrangePeel Peel { get { return new OrangePeel(); } }
    protected override OrangePips Seeds { get { return new OrangePips(6); } }
}

编辑:可以公平地说,如果我们有一个从类Ornament派生的PlasticOrange,则它只能实现IOrange,而不能实现Seeds受保护的方法。那样就好。根据定义,接口是调用者和对象之间的协定,而不是类及其子类之间的协定。就我们而言,抽象类与这个概念非常接近。那很好。您实质上建议的是该语言的另一种构造,通过该构造我们可以在不破坏构造的情况下将子类从一个基类切换到另一个基类。对我来说,这没有道理。

如果要创建类的子类,则该子类是基类的特化。它应该充分了解基类的任何受保护成员。但是,如果您突然想切换基类,则该子类应与任何其他IOrange一起使用是没有意义的。

我想您有一个公平的问题,但这似乎是一个极端的案例,老实说,我认为没有任何好处。


10
一个抽象类,只有当它是合理设置的基础-班工作NavelOrange,并ValenciaOrange以同样的事情。假设一个类PlasticOrange已经从派生Ornament
ajlane

4
然后我会怀疑橙子是否有种子或可以去皮。认真地说,这是一个公平的观点,但是我们受到OOP单继承模型的约束,因此我没有很好的答案。话虽如此,该解决方案可以解决原始问题,除非您打算创建所提示的模型。:)
Szymon Rozga,2009年

50

无法看到为什么要这样。如果希望派生类提供特定方法的实现,请使用抽象基类。接口就是接口。一份公共合同,别无其他。将接口视为规范,它描述了实现应如何看待外部世界。两针插头的规范没有说明(至少我认为是这样)它的内部结构应该是什么样的。它只是必须与插头接口兼容。 (来源:made-in-china.com插头


13
最好的解释我听说过一个界面。+1
WolfmanDragon

6
我认为您会发现知道该插头后面的实现是否需要110V或220V电流非常重要;-)
mindplay.dk 2012年

5
@ mindplay.dk在理想情况下,用于110和220的插头应该有所不同。
安东·戈戈列夫

6
@Anton在我的理想世界中,所有设备在110伏和220伏电压下都能正常工作,并且插头都相同;-)
mindplay.dk 2012年

1
接口保证了外部客户端的实现。您可以肯定地知道,如果类A实现了B,则类A将实现接口B的方法。因此,您可以将A威胁为B并确保它可以作为B起作用。抽象类保证内部客户端的实现,这些内部类派生自内部客户端,但是它也允许部分实现。正是这种双重用法是有问题的。我认为抽象类应该分为两个结构。一种用于不允许多重继承的部分实现,另一种用于允许多重继承的派生协定。
Didier A.

15

因为这没有意义。接口是公开的合同。我是IThing,因此如果有要求,我将执行IThing方法。您不能要求IThing确认它执行了无法告诉您的方法。


但是IThing可以告诉其继承者有关非public方法的信息。为什么不能在这种情况下要求它执行其他IThing方法?
ajlane

因为这些不是IThing方法,所以它们是基类的方法/属性。如果两个IThing实现实现了这个受保护的成员,而第三个实现没有实现,因为那只是没有意义,该怎么办。然后怎样呢?
Szymon Rozga,2009年

@AJ-感到困惑。一个IThing不能告诉它关于非公开的继承者,这是正确的吗?
annakata

@siz:第三个必须实现它:它在接口中。
09年

@annakata:假设AbstractThing实现了IThing,而ConcreteThing继承自AbstractThing。AbstractThing可以告诉ConcreteThing有关受保护的(非公共)成员的信息。这个想法是IThing还可以指定其中的一些内容
-ajlane

9

存在的接口允许人们访问您的类而无需知道具体的实现是什么。它与数据传递合同完全脱离了实现。

因此,接口中的所有内容都必须是公共的。非公共成员仅在您有权访问实现并且因此对接口定义没有有意义的贡献时才有用。


7

接口成员公共API。之类的东西protected等都是实现细节-和接口不具有任何实现。我怀疑您正在寻找的是显式接口实现:

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    OrangePips IOrange.Seeds { get { return null; } }
}

显式接口实现仍然是public,所以这不是我真正想要的。具体来说,我认为protected成员可以在一组基类及其继承者之间提供API。
ajlane

严格来说,这是在基类和继承者之间的-它根本与接口无关。如果要在基类中使用一个受保护的方法,只需声明一个即可。
马克·格雷韦尔

是的,但是假设我们有大量实现该接口的基类。定义一个合同是合理的,该合同规定,凡是来自这些阶层的人都可以期望获得一定的支持标准。
ajlane

2

接口就像键的形状一样。

在此处输入图片说明

这不是关键。

不是锁。

这只是苗条的接触点。

因此,接口的所有成员(定义键的形状)都必须是公共的。

对于钥匙来打开锁,重要的是它们必须具有相同的形状。

通过公开形状(界面),可以让其他人创建兼容的锁或兼容的键。

否则,将其设置为内部(接口),将不允许他人创建兼容的锁或兼容的钥匙。


1

接口是一种向客户承诺某些功能的合同。换句话说,接口的目的是能够将类型强制转换为类型并将其传递给需要该接口保证的功能的代码。由于类型的客户端代码无法访问该类型的受保护成员,因此在接口中声明受保护的项目没有任何意义。


1

与抽象基类相反,受保护的接口将允许(抽象类的)“多重继承”,我会发现它有用一次或两次。


0

接口是关于某个对象可以做什么的全部内容,因此,当使用实现该接口的类时,开发人员将期望所有成员都得到实现,因此受保护的访问修饰符对于接口不会有任何意义。


1
同意,但是我不是在问为什么实现接口的成员不是可选的(因为他们不应如此),而是为什么我不能指定其访问级别。
09年

0

当前接口设计中有一个合理的判断,那就是它为实现者提供了更大的灵活性。请记住,接口通常是由框架程序员编写的,而实现者则是不同的人。强制实施将是不必要的苛刻。


0

通过实现接口,类型声明其支持一组特定的方法。如果这些方法中的任何一个都不公开,则调用者将无法使用该方法,因此该类型将不支持所述接口。


0

接口仅包含公共成员。受保护意味着您声明的内容仅对类和派生类实例可用。


“方法”应读为“成员”-也可以是属性和事件
马克·格雷夫

0

任何实现.net接口的类都必须包括所有接口成员的实现。此外,任何类都可以将其希望的任何成员公开给派生类。要求接口的实现必须包括只能在派生类中使用的成员将没有任何用处,除非(1)接口外部的某些成员可以看到这样的成员,或者(2)接口的实现可以使用他们自己没有定义的成员。如果允许接口包含嵌套类(可以访问接口的protected成员),则protected接口成员会很有意义。确实,如果嵌套在接口中的类可以为该接口定义扩展方法,则它们可能非常有用。不幸的是,不存在这样的设施。

顺便说一句,即使不能将类嵌套在接口中,将internalaccess修饰符应用于接口成员仍然很有用,其结果是只有定义了接口的程序集才可以为其定义任何实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.