Framework Design Guidelines: Sealed Classes
Continuing in our weekly blog post series that highlights a few of the new additions to the Framework Design Guidelines 2nd edition.. This content is found in the Extensibility Mechanisms section of Chapter 6: Designing for Extensibility. It is interesting to watch as new development methodologies become more popular and how the color the guidelines…
CONSIDER using unsealed classes with no added virtual or protected members as a great way to provide inexpensive yet much appreciated extensibility to a framework.
Developers often want to inherit from unsealed classes so as to add convenience members such as custom constructors, new methods, or method overloads. For example, System.Messaging.MessageQueue is unsealed and thus allows users to create custom queues that default to a particular queue path or to add custom methods that simplify the API for specific scenarios (in the following example, the scenario is for a method sending Order objects to the queue).
public class OrdersQueue : MessageQueue {
public OrdersQueue() : base(OrdersQueue.Path){
this.Formatter = new BinaryMessageFormatter();
}
public void SendOrder(Order order){
Send(order,order.Id);
}
}
Because Test Driven Development has caught fire in the .NET Developer community, many developers want to inherit from unsealed classes (often dynamically using a mock framework) in order to substitute a test double in the place of the real implementation.
At the very least, if you’ve gone to the trouble of making your class unsealed, consider making key members virtual, perhaps via the Template Method Pattern, in order to provide more control.
Comments
Anonymous
January 19, 2009
PingBack from http://blog.a-foton.ru/index.php/2009/01/20/framework-design-guidelines-sealed-classes/Anonymous
January 19, 2009
This looks dangerous to me. Unless OrdersQueue really IS-A MessageQueue, that is. In C++ at least, deriving from a concrete class should be private (my experience). Can't this be solved with extension methods instead? Convenience constructors can be provided with factory methods. Regards, Daniel Lidström Stockholm, SwedenAnonymous
January 19, 2009
Thank you for putting this in the guidelines! The often extreme focus Micrsoft has had on babysitting developers by sealing every class is a quite annoying practice.Anonymous
January 20, 2009
Leaving types unsealed is great, although I'm not sure about promoting the use of inheritance without first suggesting composition as a better alternative. Also, Phil's comment about making key members virtual and promoting the use of the Template Method pattern seems a tad dangerous; what are 'key members'? Anything public? Anything protected? Surely the key members are specific to why you're deriving from the class in the first place? I'd hate to see lots of abstract classes introduced into the Framework to facilitate the Template Method pattern.Anonymous
January 20, 2009
Interesting post. However, the title of the post gave me the expectation of providing a guideline on when to write sealed classes, not unsealed classes.Anonymous
January 21, 2009
The comment has been removedAnonymous
January 23, 2009
Thank you for submitting this cool story - Trackback from DotNetShoutoutAnonymous
January 24, 2009
On of the big issues with leaving classes un-sealed and members abstract or virtual is performance. The CLR will perform a special set of optimisations for a class if it's sealed to make it super-fast (the CLR doesnt have to worry about polymorphic access by super-classes). Also if a member's not virtual, the CLR can perform optimisations for it too. Classes should be sealed by default and then unsealed by design. Static classes are particularly performant, because they are compiled as abstract and sealed, exposing on static members. DeanAnonymous
March 30, 2009
Ick, no. I'm one of the crowd that think C# should seal classes by default. Inheritance is a powerful tool, but can be a pain if it's not thought about carefully. If you don't design for inheritance, you shouldn't permit it. Designing for inheritance is hard, and limits your implementation freedom later. Do you really want to document everywhere that the implementation calls a virtual method? You can't change your mind later - someone may be depending on that behaviour. (If you don't document it, they shouldn't depend on it but it becomes unexpected; if you do document it, you'd be breaking the contract.) If you want to be able to use a test double for a class, make it implement an interface and use the interface as the dependency. If you want to "add" methods to simplify the API, but without adding any extra data and only using the public API, use extension methods - they're great for that. Inheritance should be for specialization IMO, which doesn't really cover either of the reasons given here. For a more intelligent explanation of my point of view, read CLR via C# or Effective Java, both of which are written by smarter people than me :) Jon