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

问题描述 投票:65回答:12

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

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; 
    ...
}

(不可否认,这对structs不起作用)

我不能在接口中看到privateinternal修饰符的大量情况,但支持publicprotected修饰符似乎是完全合理的。


我将尝试通过将它们与protecteds完全分开来解释interface成员在interfaces上的效用:

让我们设想一个新的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成员的类 - 这不是我们目前可以做的事情。

c# interface protected access-modifiers
12个回答
57
投票

我想每个人都认为界面只有公共成员,没有实现细节。你要找的是abstract class

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); } }
}

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

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

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


0
投票

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


0
投票

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


0
投票

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

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


48
投票

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


15
投票

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


9
投票

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

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


7
投票

接口成员是公共API;像protected等的东西是实现细节 - 并且接口没有任何实现。我怀疑你要找的是显式接口实现:

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

1
投票

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


1
投票

界面就像一个键的形状。

enter image description here

这不是关键。

这不是锁。

这只是苗条的接触点。

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

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

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

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


0
投票

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


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.