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

ajl*_*ane 67 c# interface protected access-modifiers

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

public interface IOrange
{
    public OrangePeel Peel { get; }
    protected OrangePips Seeds { get; }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,接口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); } }
}
Run Code Online (Sandbox Code Playgroud)

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

public class SpecialNavelOrange : NavelOrange
{
    ...
    // Having a seed value is useful to me.
    OrangePips seeds = this.Seeds; 
    ...
}
Run Code Online (Sandbox Code Playgroud)

(不可否认,这不适用于structs)

我不能在界面中看到很多private或者internal修饰符,但是支持publicprotected修饰符看起来非常合理.


我将通过将它们与s完全分开来尝试解释protected成员的效用:interfaceinterface

让我们设想一个新的C#关键字support来强制执行继承者契约,以便我们按如下方式声明:

public support IOrangeSupport
{
    OrangePips Seeds { get; }
}
Run Code Online (Sandbox Code Playgroud)

这将允许我们收缩类以向其继承者提供受保护的成员:

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

这不是特别有用,因为类通过首先提供protected成员已经暗示了这个合同.

但是我们也可以这样做:

public interface IOrange : IOrangeSupport
{
   ...
}
Run Code Online (Sandbox Code Playgroud)

从而适用IOrangeSupport于所有实现IOrange并要求他们提供特定protected成员的类 - 这不是我们目前可以做的事情.

Szy*_*zga 58

我想每个人都认为界面只有公共成员,没有实现细节.你在寻找的是一个抽象的课程.

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); } }
}
Run Code Online (Sandbox Code Playgroud)

编辑:公平地说,如果我们有一个派生于类装饰的PlasticOrange,它只能实现IOrange而不是Seeds保护方法.那样就好.根据定义,接口是调用者和对象之间的契约,而不是类及其子类之间的契约.抽象类和我们这个概念一样接近.这没关系.你基本上提出的是语言中的另一个构造,通过它我们可以在不破坏构建的情况下将子类从一个基类切换到另一个基类.对我来说,这没有意义.

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

我想你有一个公平的问题,但这似乎是一个极端的案例,我认为没有任何好处.

  • 只有将`NavelOrange`和`ValenciaOrange`的基类设置为相同的东西是合理的,抽象类才有效.假设一个类'PlasticOrange`已经来自`Ornament`. (7认同)
  • 然后我怀疑橙子有种子还是能剥皮.值得注意的是,这是一个公平的观点,但我们受到OOP单继承模型的限制,所以我没有好的答案.话虽这么说,这个解决方案解决了原始案例,除非你打算创建你暗示的模型.:) (4认同)

Ant*_*lev 49

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

  • 最好的解释我听说过一个界面.+1 (13认同)
  • @Anton在_my_完美世界中,所有设备在110和220伏特上同样运行良好,_plugs_将全部相同;-) (6认同)
  • 我想你会发现知道插头背后的实现是否需要110V或220V电流是非常重要的;-) (5认同)
  • @ mindplay.dk在一个完美的世界中,110和220的插头应该是不同的. (5认同)
  • 接口保证外部客户端的实现。您肯定知道,如果 A 类实现了 B,那么 A 类将实现接口 B 的方法。因此,您可以将 A 作为 B 来威胁,并保证它会像 B 一样工作。抽象类保证内部客户端、派生自它的类的实现,但它也允许部分实现。正是这种双重用途产生了问题。我认为抽象类应该分为两个构造。一种用于不允许多重继承的部分实现,另一种用于允许多重继承的派生契约。 (2认同)

ann*_*ata 15

因为没有意义.界面是公开的合同.我是一名IThing,因此如果被问到我会执行IThing方法.您不能要求IThing确认它执行它无法告诉您的方法.


Abd*_*mal 11

自 C# 8.0(2019 年 9 月)起,您可以在接口内拥有访问修饰符

检查接口 c# 8.0 中的这些更改

使用 C# 8.0 中的默认接口方法更新接口

查看这些帖子

C# 8 接口:公共、私有和受保护的成员

仔细看看 C# 8 接口


Jar*_*Par 9

存在允许人们访问您的类而不知道具体实现是什么的接口.它完全将实现与数据传递合同分开.

因此,界面中的所有内容都必须是公共的.非公共成员仅在您有权访问实现时才有用,因此不会对接口定义做出有意义的贡献.


Mar*_*ell 7

接口成员公共API; 之类的东西protected等都是实现细节-和接口不具有任何实现.我怀疑你要找的是显式接口实现:

public class NavelOrange : IOrange
{
    public OrangePeel Peel { get { return new OrangePeel(); } }
    OrangePips IOrange.Seeds { get { return null; } }
}
Run Code Online (Sandbox Code Playgroud)