- 6.1 Extensibility Mechanisms
- 6.2 Base Classes
- 6.3 Sealing
- Summary
6.3 Sealing
One of the features of object-oriented frameworks is that developers can extend and customize them in ways unanticipated by the framework designers. This is both the power and the danger of extensible design. When you design your framework, it is very important to carefully design for extensibility when it is desired, and to limit extensibility when it is dangerous.
Sealing is a powerful mechanism that prevents extensibility. You can seal either the class or individual members. Sealing a class prevents users from inheriting from the class. Sealing a member prevents users from overriding a particular member.
public class NonNullCollection<T> : Collection<T> { protected sealed override void SetItem(int index, T item) { if(item==null) throw new ArgumentNullException(); base.SetItem(index,item); } }
Because one of the key differentiating points of frameworks is that they offer some degree of extensibility, sealing classes and members will likely feel very abrasive to developers using your framework. Therefore, you should seal only when you have good reasons to do so.
DO NOT seal classes without having a good reason to do so.
Sealing a class because you cannot think of an extensibility scenario is not a good reason. Framework users like to inherit from classes for various nonobvious reasons, such as adding convenience members. See section 6.1.1 for examples of nonobvious reasons users want to inherit from a type.
Good reasons for sealing a class include the following:
The class is a static class. For more information on static classes, see section 4.5.
The class inherits many virtual members, and the cost of sealing them individually would outweigh the benefits of leaving the class unsealed.
The class is an attribute that requires very fast runtime look-up. Sealed attributes have slightly higher performance levels than unsealed ones. For more information on attribute design, see section 8.2.
DO NOT declare protected or virtual members on sealed types.
By definition, sealed types cannot be inherited from. This means that protected members on sealed types cannot be called, and virtual methods on sealed types cannot be overridden.
CONSIDER sealing members that you override.
public class FlowSwitch : SourceSwitch { protected sealed override void OnValueChanged() { ... } }
Problems that can result from introducing virtual members (discussed in section 6.1.4) apply to overrides as well, although to a slightly lesser degree. Sealing an override shields you from these problems starting from that point in the inheritance hierarchy.
In short, part of designing for extensibility is knowing when to limit it, and sealed types are one of the mechanisms by which you do that.