我能想到两个理由,支持使用受保护的属性而不是受保护的字段。首先,考虑以下示例:
public class BaseClass
{
protected readonly List<string> _someValues;
}
public class InheritedClass : BaseClass
{
public void NeedsThoseValues()
{
DoSomethingWith(_someValues);
}
}
现在您决定更改基类以使用延迟实例化:
public class BaseClass
{
protected readonly Lazy<List<string>> _someValues;
}
现在继承类必须更改以调用
_someValues.Value
。那些继承类真的需要更改吗?如果字段是私有的,并作为属性暴露给继承类,更改基类不会破坏继承类:
public class BaseClass
{
private readonly Lazy<List<string>> _someValues;
protected List<string> SomeValues => _someValues.Value;
}
这需要我们改变我们心中继承类的形象。我们可能开始将其视为在较小的房子周围建造一个更大的房子。所有原来在较小的房子里的东西都在更大的房子里,所以它们实际上是一个大房子。没有理由将内部房屋隐藏起来不让外部房屋看到。
实际上并不是这样的。基类是它自己独立的实体,存在于更大的房子中。为了在多个房子(多个继承类)中重用它,我们需要封装它,使得继承类对它了解得越少越好,就像它们依赖的任何其他类一样。
这有时会创建一种奇怪的关系。我们试图防止继承类和基类的实现细节之间过度耦合,但它们却是耦合的,因为继承类不能没有基类存在。这引发了一个问题 - 为什么继承类需要与基类具有这种关系呢?为什么不将基类所做的事情分离成它自己的类,用抽象(比如接口)表示,并在需要时注入它呢?
这就是偏向组合而非继承的思想。很多时候,继承被用于在类(基类和子类)之间共享功能,但这并不是继承的目的。如果我们有两个不同的功能区域,我们应该使用两个不同的类来实现,并且一个类可以依赖于另一个类。如果我们通过继承来实现这一点,当我们的类需要依赖于另一个类时,我们将遇到麻烦。我们不能再给它一个基类了。除非我们组合不同的类以协同工作,否则我们可能会开始做一些非常恶劣的事情,比如将更多的功能添加到现有的基类中或创建更多的继承级别。这样做会让人难以理解,最终会把我们逼入死角。
另一个原因是,当某人在基类中看到
_someValues
的引用时,他们会认为它是在使用它的类中声明的字段,因为这是更常见的约定。这不会造成太大的混淆,但他们需要花费更长的时间来弄清楚。
这些偏向于使用受保护的只读属性而不是受保护的只读字段的原因,在许多特定情况下可能并不是问题。但很难事先知道,因此最好选择首选的做法,并养成习惯,同时理解为什么要这样做。
public
和protected
之间实际上没有区别。两者都可以在声明程序集外部看到。因此,关于public readonly X Y
与public X Y{get;}
的所有争论都适用。 - CodesInChaos