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

90

声明接口中的受保护访问成员有什么反对意见?例如,以下代码是无效的:

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修饰符没有太多意义,但支持publicprotected修饰符似乎非常合理。


我将尝试通过将interfaceprotected成员分开来解释interfaceprotected成员的实用性:

假设有一个新的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 成员——这是目前我们无法做到的。


可能是Non Public Members for C# Interfaces的重复问题。 - nawfal
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
我很惊讶没有任何答案或评论提到EIMI。当成员从实现类型的角度来看时,EIMI使接口成员变为私有。 - qqqqqqq
1
可能会在C# 8.0中出现 https://jeremybytes.blogspot.com/2019/11/c-8-interfaces-public-private-and.html - Doug Domeny
15个回答

0
接口主要关注一个特定对象能做什么,因此当使用实现该接口的类时,开发人员期望所有成员都被实现,所以对于接口来说,protected访问修饰符没有任何意义。

1
同意,但我并不是在问为什么接口的实现成员不能是可选的(因为它们不应该是可选的),而是为什么我不能指定它们的访问级别。 - ajlane

0

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


“methods” 应该改为 “成员” - 也可以是属性和事件。 - Marc Gravell

0

当前接口设计中存在着明智的判断,即它提供了实现者更大的灵活性。请记住,很多时候接口是由框架程序员编写的,而实现者则是不同的人。强制执行实现将是不必要的严厉。


0
通过实现接口,类型声明它支持一组特定的方法。如果其中任何一个方法不是公共的,它将不可用于调用者,因此该类型将不支持所述接口。

0
任何实现 .net 接口的类都必须包含所有接口成员的实现。此外,任何类都可以向派生类公开其希望公开的成员。要求接口的实现必须包括仅可从派生类中使用的成员将没有任何有用的目的,除非 (1) 这样的成员可以对接口外的某些东西可见,或者 (2) 接口实现可以使用它们自己没有定义的成员。如果允许接口包含嵌套类(这些类可以访问接口的 protected 成员),那么 protected 接口成员就会有意义。事实上,如果嵌套在接口中的类可以为该接口定义扩展方法,则它们可能非常有用。不幸的是,没有这样的设施存在。
顺便说一下,即使不能在接口中嵌套类,将 internal 访问修饰符应用于接口成员仍然很有用,其效果是只有定义接口的程序集才能为其定义任何实现。

将接口成员限制为派生类将非常有用。这样,实现就负责如何公开或不公开它。接口不允许定义范围,因此应由实现类决定。如果我想要一个类成员将类型化接口成员公开为类型或其派生物,则应由类而不是接口来决定。被阻止这样做真的很糟糕。 - Suncat2000
如果将派生类引用转换为基类类型,然后再转换为接口类型可以允许调用接口成员,那么将基类引用转换为接口类型也必须能够允许调用该成员。如果只有在将派生类引用转换为接口类型时才能使用接口成员,则具有基类型引用的代码要使用接口方法并想要使用接口,就必须将其转换为正确的派生类型,这将破坏使用接口的目的。 - supercat

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