Breaking through the class sealing

Do you understand ‘sealing’ in C#? Somewhat? Anyway, here’s the lowdown.

I’ve done this article from a C# perspective, but I’ve occasionally referenced .NET when appropriate.

What is sealing a class?

By sealing a class in C#, you ensure that you ensure that no class can be derived from that class. You do this by simply adding the word ‘sealed’ to a class definition: public sealed class Dog {}

Now writing something like ” public sealed class Hamster: Dog {} ” you’ll get a compile error like this:

‘Hamster: cannot derive from sealed type ‘Dog’

If you look in an IL disassembler, you’ll see a definition like this:

Note the addition of the word ‘sealed’.

What about sealing methods?

You can also seal overriding methods. By adding the word ‘sealed’, you ensure that the method cannot be overridden in a derived class. Consider the following code:

public class Dog : Mammal { public sealed override void Go() { } }
public class Mammal { public virtual void Go() { } }

In this code, the method ‘Go’ in Dog is sealed. It cannot be overridden in a subclass. Writing this would cause a compile error:

public class Dachshund : Dog { public override void Go() { } }

However, we can ‘new’ a method with the same name. This is essentially a new method; distinct from the ‘Go’ in the subclass:

public class Terrier : Dog { public new void Go() { } }

Sealing properties?

You can also seal seal properties. You add ‘sealed’ to the property definition, like so:

public sealed override string Name
get { return m_Name; }
set { m_Name = value; }

In C#, you can only seal a property, not the underlying setters/getters. This is because C# offers no override syntax for setters or getters. However, in underlying IL you seal the setter and getter methods individually – a property is just metadata.

Why bother sealing?

There are a few traditional reasons to seal:

  1. Invariance. Other people may want to derive from your class, even though your implementation may make successful derivation near-impossible. There may be twisted, hacky logic that could never be second-guessed by another developer. By sealing your class, you’re protecting them from wasting their time. The CLR team has sealed most of the framework classes, and I assume they did this for this reason.
  2. Security. By deriving from your type, an attacker may gain access to functionality that enables him to hack your system. I consider this a very weak security precaution.
  3. Speed. If a class is sealed, then .NET doesn’t need to consult the virtual-function-call table to find the actual type, since it knows that no derived type can exist. Therefore, it could emit a ‘call’ instead of ‘callvirt’ or at least optimise the machine code, thus producing a performance benefit. But I’ve done trials, and have been unable to demonstrate this If you have an example, please share!

All in all, I’m not convinced that sealing is interesting or important. Anyway, moving-on…

What is automatically sealed?

  1. Value types and structs. If they were not always sealed, all sorts of things would go wrong. For instance, structs are laid-out inline within a class. But what if you assigned a substruct to a struct field of that class? There may be too many fields to fit.
  2. Static classes. Static classes exist in C# but not .NET. The C# compiler compiles a static class into an ‘abstract sealed’ class. So static classes are already sealed in C#.
  3. Enumerations. The CLR does not track the types of enumerations – it treats them as simple value types. Hence, polymorphism would not work.

What cannot be sealed?

  1. Interfaces. Interfaces exist to be implemented, so sealing to prevent implementation is dumb. But what if you could prevent interfaces from being extended (i.e. ban declarations like “public interface IMyInterface : ISealedInterface”)? There is no good reason to seal an interface like this. Sealing finalizes behaviour, but interfaces have no intrinsic behaviour to finalize

  2. Abstract classes. In IL you can create an abstract sealed class. But C# syntax for this already exists – declaring a class as a ‘static’, so it forces you to declare it as such.

  3. Non-override methods. If a method isn’t declared as override it cannot be overridden, so sealing would make no difference. Note this is stated from a C# perspective – the words are opposite in IL. In IL, you have four choices in total: no declaration (which actually seals the method), ‘virtual’ (called ‘override’ in C#), ‘sealed virtual’ (‘sealed override’ in C#) and ‘newslot virtual’ (‘new virtual’ or ‘virtual’ in C#, depending on whether the method already exists in a base class).

  4. Methods that implement interface methods. Methods that implement an interface method must be virtual, so cannot be sealed.

  5. Fields. A field cannot be overridden, only hidden (using the ‘new’ keyword in C#), so sealing would make no sense.