Wednesday, October 26, 2005

Do Sealed Classes Terminate Innovation?

Update: I accidently published an old draft of this. Sorry about that.

One thing that gets a lot of debate in .NET is Sealed Classes.

Nick Hodges just posted More on Sealed Classes. He says they are "irritating and silly". Do sealed classes serve a purpose beyond annoying developers? How much of the .NET Framework is sealed? Is the sealing justified?

In object orientated programming (OOP) a developer can extend a class created by another developer adding additional functionality. This functionality can be to create a more specialized version of the class, or to add more generally useful functionality that the original developer didn't think of. Either way, this ability to reuse and extend is a great advantage of OOP. There are things a developer can do to make it easier to extend their base class, like declaring a method virtual to allow future versions to override that behavior.

This functionality of OOP is actually similar to what has been going on in botany and agriculture for ages. Seeds are planted, and when the plants come to maturity they produce new seeds. The seeds of the best plants are then taken and replanted. This selection of only the best plants by the farmer / botanist is similar to a programmer extending a class, except it is done through natural genetic variations. The farmer just chooses which of the variations is most desirable and re-uses those seeds. You see the same thing in animal husbandry too.

Along comes .NET with its sealed class. When a class is sealed then future developers are prevented from extending the base class defined by the original developer. So if a developer wants to create a more specialized version of a class, or add functionality not conceived of by the original developer they are out of luck. Instead they must recreate all the original functionality of that base class, or create a helper class which performs the new functionality on the base class. Both options are less then optimal.

Unfortunately many of the classes in the .NET framework are sealed. Just within the System namespace and mscorlib assembly (the main part of .NET CLR) there are well over 300 sealed classes. (If someone else has better data please let me know!) That is compared to just over 450 public classes. Almost half (40%) of the classes are sealed. Many developers complained about all the sealed classes. It seemed like every time a developer wanted to extend a class they only discovered it was sealed.

Again we can compare this to field of botany. Monsanto is a company that makes specialized and engineered seeds. They were researching what they called the "terminator gene." Any seed that contained this gene would grow a plant like normal, but any seeds that plant produced would be sterile and would not grow. This would prevent any farmers from leveraging their crop for future crops. When the public discovered their plans there was a huge outrage. In India, where Monsanto was testing crops, the local villagers would sneak onto the field and burn the entire crop. Eventually Monsanto announced that they would not include the terminator gene.

Many developers complained about the sealed classes. It was hoped that Microsoft would feel the pain of the developer community and unseal many of the classes in the .NET 2.0 release. Unfortunately the number of public sealed classes jumped to well over 500 in .NET 2.0 (Beta 2). When we compare this to the number of public classes we see the gap narrowing as there are only just over 600 of those. Putting sealed public classes at 45%, an approximate 5% increase over .NET 1.1. So instead of improving the situation things only got worse.

Buy The C# Programming Language from Amazon.comSo why would Microsoft want to seal their classes and stifle the ability of developers to innovate and leverage the the framework? Consulting The C# Programming Language by Anders Hejlsberg, Scott Wiltamuth and Peter Golde (Anders lead the team to develop C#) it says the following about Sealed Classes (page 275):

10.1.1.2 Sealed Classes

The sealed modifier is used to prevent derivation from a class. [. . .]

The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances to nonvirtual invocations.

So sealing a class can provide some performance enhancements when a class has a number of virtual methods. It would seem if that is the case then a well designed framework would have a sealed version of classes that did not introduce any functionality over the non-sealed ancestor, but only provided a sealed version for speed. Then future developers could leverage all the functionality by descending from the non-sealed ancestor, later creating a sealed version of their derived class for the speed improvement.

This would be similar to how Borland designed the VCL. There is a "Custom" version of each control. The Custom version has all the properties as protected. Then there is a version that descends from the Custom version that only publishes the necessary properties. Then if someone wants to expand on the control they simple descend from the Custom version and they can then choose which properties to make public (this is significant since it is a hack to un-publish a published property, and you cannot move something to a lower visibility level (e.g., move from Public to Protected).

Buy Framework Design Guidelines from Amazon.comI wonder what advice Krzysztof Cwalina and Brad Abrams (who are involved with the internal .Net framework design at Microsoft) have to say about the use of sealed classes. If we look in their book Framework Design Guidelines under the section on sealed classes (6.4 on page 174 - 177) we see the following:

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 idea. [. . .]

Good reasons for sealing a class include the following:

  • The class is a static class. [. . .]
  • The class stores security-sensitive secrets in inherited protected members.
  • 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 [. . .]

In the book Brad also points out the advantages of an open and customizable framework over the Win32API where there is no ability to extend an API call. In a discussion on Designing .NET Framework Class Libraries Krzysztof also said "The general guidelines say not to seal types but rather try to seal individual (all) methods."

Brad also published portions of the internal Design Guidelines document to his blog, where it says the following:

FxCop Rule (draft): Flag non-sealed public types that introduce virtual members with the message: "Virtual members represent points of specialization in your type. Great care should be taken in exposing virtual members, please see the design guidelines document on this topic and fully consider the ramifications."

It goes on to basically to encourage sealing to prevent extensibility. So it encourages a non-extensible framework. This seems to fly in the face of the advice given in the book that says "Sealing a class because you cannot think of an extensibility scenario is not a good idea."

Monsanto claimed similar reasons to include the terminator gene. If one of their genetically modified strains was to cross with another strain then the results could be undesirable and at least unexpected. By ensuring that all genetically modified plants cannot reproduce then they eliminate this possibility. The other reason was that it protected the intellectual property of the companies engineering improved seeds. By protecting their intellectual property it would encourage greater innovation which benefits everyone.

Since Microsoft did not adopt the obvious course of action of only sealing classes that do not introduce new functionality therefore providing the speed boost of a sealed class without the restriction then we must assume speed was not their primary motivator. Should we then assume that it was for security? Is this part of their move to more secure code?

My opinion is that Microsoft sealed classes because they could, and not because it was a good idea.

Subject Tags: [] [] [] [] [] [] []

5 comments:

Anonymous said...

They haven't made a convincing case. It's quite suspicious.

Anonymous said...

Also...

it's typical of Microsoft to tell us to do one thing, while they do another.

unused said...

Someone asked if there is a performance boost to sealing a class.

Actually yes, if the class is sealed, or all the virtual methods are sealed, then it is faster. That is why I suggest introducing a descendant that only seals the class without introducing any additional functionality.

The reason sealing a class is faster then leaving virtual methods is that virtual methods must remain virtual when initialized since the object doesn't know if they will later be overridden. Then it must consult the virtual method table (or .NET's version of one) to see where the method is actually defined. Sealing it takes the method out of the virtual method table.

I am yet to see any hard data that shows the speed improvement of a sealed classes offer one with virtual methods. I am also yet to see the speed difference in introducing another level of ascendancy that I suggested. If leaving virtual methods really is that big of a performance hit then maybe Microsoft should look at a way to improve their virtual method look-ups instead of just sealing everything.

Brad Abrams took the time to point out the advantages of an extensible framework over an API like Win32. You would think Microsoft would provide such extensibility.

unused said...

Microsoft does not identify "protecting intellectual property" as a valid reason for sealing a class, but it is obviously what they attempted. They are ineffective at that though since anyone can run Although with a program like Reflector.FileDisassembler to get to the source code. All sealing does it makes developers using their API less productive.

They then encourage others to keep their API's open to make developers more productive.

If you are providing an API for programmers to develop with why would you want to limit how they can use it? If they are making a call to your API then when something fails they can still blame you. The only way to prevent that from happening is to not provide them an API in the first place.

The use of the term "Terminate" was a reference to the Terminator Gene I mentioned.

Anonymous said...

Inheritance, while an implementation detail, is one of the basics of OO, to not have it available can be crippling.

Sealed classes suck. I needed to extend ImageFormat to include SVG and XAML, but no, can't do it. I had to write my own, no it didn't take long, but now I have to maintain the code. And everytime I turn around it seems like MS is putting crap in our way.

I think that the community should write their own .Net library. In regards to the Borland comment, I had the source code and could change the behavior if needed.

And, in agreement with Jim, who gives a crap if there is a performance boost. If someone needs a performance boost then MS should release the code so that we can unseal the classes. Or as Jim suggest make there virtual lookups faster.

What's wrong with protecting their API, nothing, except they're not as Jim pointed out. Although I suppose I'm breaking some precious law if I do disassemble their assembly. I'll just stop paying for it as soon as possible.

Going back to my ObjectiveC coding, and happiness.