嗯,他们这样做是因为这就是他们的目的。我想也许你想知道为什么一个人可能想要。简单的答案是因为派生类需要访问它们,而外部类不需要。
访问修饰符(以及public 和protected,还有private、internal 和protected internal)是一种使代码尽可能易于理解以减少错误的方法。
有些语言没有任何形式的封装。在最极端的情况下,任何代码都可以更改任何数据的任何部分。训练有素的编码人员将减少对给定类型数据进行操作的位置数量,但可能仍然不明显所有可能使对象*处于不同状态的操作组合。当他们的代码被用作其他人代码的一部分时,情况会变得更糟。
访问修饰符帮助我们处理这个问题。我们默认将成员设为私有。那么唯一可以访问成员的地方就是类本身。这意味着:
- 在如何操作它们时,唯一的错误可能是在类内部。
- 唯一需要担心使这些成员保持一致状态的代码位于类内部,并且该代码不必担心其他代码可能会破坏它。
- 您可以通过查看一个类的定义来全面了解这些字段的各种操作方式,该定义通常在一个文件中,很少超过两个。
这让我们更容易编写好代码。
当然,所有内容都是私有的类并不是很有用。我们通常必须让一些成员公开。通常我们的字段是私有的,一些有用的方法是私有的,然后一些公共方法和属性使用这些。我们仍然可以通过只检查一个类来检查对私有成员的所有可能操作,尽管我们已经开放了调用其他类的成员。因此,这些成员为我们提供了类内部和外部代码之间的接口,这是我们保护类状态免受错误同时为其他代码提供有用功能的边界。
现在应该很清楚,我们不会公开某些东西,除非我们需要,但我们确实需要让有用的工作成为可能。
使成员受保护为我们提供了一个中间立场。我们仍在减少一些可以被操纵的地方,但不是那么严重。通常,这样做是为了让派生类可以为基类中定义的通用接口提供自己的机制。
使用它的情况较少,因为通常我们可以将内容保密 - 这更安全 - 或者必须公开才能有用。最常见的情况之一是公共成员提供功能而受保护的定义方式来实现它。例如,HttpEncoder 提供了几种处理 HTML 字符串编码问题的方法,但是派生类覆盖了两个受保护的抽象方法,以提供几种不同方法共有的功能。外部类不需要访问这些,但派生类可以。
一个实际的例子。假设我们有一个实现INotifyPropertyChanging 的基类。此接口意味着它必须跟踪PropertyChangingEventHandler 处理程序,并在属性即将更改时引发事件。
我们不希望外部班级引发该事件,因为这不关他们的事,让他们这样做只会导致错误。
我们必须让派生类这样做,因为它们可能定义自己的属性,而基类不知道。
因此,我们在这个引发事件的基类中定义了一个受保护的方法。外部类不能调用它(降低被错误调用的风险),但派生类可以(能够完成他们需要做的工作)。
*具有面向对象背景的人甚至可能不认为这些数据是“对象”。