Click here to Skip to main content
12,621,450 members (30,734 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

61K views
18 bookmarked
Posted

C# Tweaks – Why to use the sealed keyword on classes?

, 12 Aug 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
Why to use the sealed keyword on classes?

The sealed keyword is one of the very seldom used modifiers in C#. Probably most of you know what it is for, but many developers have not ever used it.

See C# Programmers Guide if you are not sure you remember what the keyword is good for: sealed keyword (MSDN).

Why Shall I Use It?

Most popular, but not really most important motivation is the performance – JIT compiler can produce more efficient code by calling the method non-virtually. I remember someone even made performance measurements, but I think that the real performance gain highly depends on algorithms in a specific use case.

The vast majority of .NET programmers let their classes “unsealed” without even considering making them sealed. If a class was not designedly made inheritable, it is very probably even impossible to inherit from it and override members reasonably. On the other hand, overriding members of the class which were not designed to be overridden might cause unpredictable results.

When a class is originally sealed, it can change to “unsealed” in the future without breaking compatibility.

Something New I’ve Discovered Recently

Recently, I was refactoring some component with multiple classes making intensive use of inheritance. During cleanup, I changed all leaf classes in inheritance tree, the classes which can not be inherited anymore, to be sealed. I was sure it will not break compatibility, but the next compile failed.

The reason was a bug, which became visible only after I made some class sealed to compile time. Not being sealed, it would throw an exception during execution. This sample demonstrates the simplified version of this situation:

interface IInterface1 {}

class Class1 {}

class Program
{
   static void Main(string[] args)
   {
     //Class1 does not implement IInterface1
     Class1 instanceOfClass1 = new Class1();

     //However this cast does not leads to compilation error
     IInterface1 someImplementer = (IInterface1)instanceOfClass1;
   }
}

Class1 does not implement IInterface1, however the cast of an instance of the Class1 to IInterface1 does not lead to compilation error. The reason is that theoretically some inherited class of the Class1 might implement this interface.

Now let’s make Class1 sealed. Now the compiler will see that Class1 can be only Class1 “itself” (and its base classes if applicable) and it does not implement interface IInterface1.

internal interface IInterface1 {}

sealed class Class1 {}

class Program
{
    static void Main(string[] args)
    {
        //Class1 does not implement IInterface1
        Class1 instanceOfClass1 = new Class1();

        //However this cast does not leads to compilation error
        IInterface1 someImplementer = (IInterface1)instanceOfClass1;
    }
}

The following compilation error will occur:

Cannot convert type 'Class1' to 'IInterface1'

So using sealed keyword brings not only performance win and additional code access security but also helps to write bug free code, making better use of the .NET type safety.

My recommendation: Always declare a new class as sealed until you are writing an abstract class or a class which must be inherited per design. Most classes in a real application (except you are writing a widely used library) can be made sealed.

P.S. You can apply the sealed keyword not only to classes but also to some members. I am going to post about that as well.


License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

George Mamaladze
Software Developer
Germany Germany
Tweeter: @gmamaladze
Google+: gmamaladze
Blog: gmamaladze.wordpress.com

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralMy vote of 5 Pin
Rohit Sardana27-Sep-16 19:47
professionalRohit Sardana27-Sep-16 19:47 
GeneralMy vote of 5 Pin
Mohammad Khalid117-May-16 21:49
memberMohammad Khalid117-May-16 21:49 
AnswerNot a good idea Pin
TobiasLawrenz27-Jan-15 3:45
memberTobiasLawrenz27-Jan-15 3:45 
QuestionMy Vote of 5 Pin
ashok rathod23-Jul-14 21:35
professionalashok rathod23-Jul-14 21:35 
GeneralNice Article Pin
Amey K Bhatkar25-Mar-14 20:18
professionalAmey K Bhatkar25-Mar-14 20:18 
GeneralMy vote of 5 Pin
Tarek Elqusi4-Mar-13 23:01
memberTarek Elqusi4-Mar-13 23:01 
GeneralMy vote of 5 Pin
nikhil _singh27-Jul-12 2:59
membernikhil _singh27-Jul-12 2:59 
GeneralMy vote of 1 Pin
SAKryukov3-Jun-12 22:47
mvpSAKryukov3-Jun-12 22:47 
GeneralRe: My vote of 1 Pin
Paulo Zemek6-Jun-12 8:08
mvpPaulo Zemek6-Jun-12 8:08 
GeneralRe: My vote of 1 Pin
Sergey Alexandrovich Kryukov29-Jun-12 10:09
mvpSergey Alexandrovich Kryukov29-Jun-12 10:09 
GeneralRe: My vote of 1 Pin
Paulo Zemek29-Jun-12 10:23
mvpPaulo Zemek29-Jun-12 10:23 
There is a difference. But that depends on the use.

For example, create a class A with a single virtual method.
Then create a class B that inherits from A.
If you do B b = SomeNonInlineMethodThatReturnsB();
And then call the B method thousands of times you can measure.
Mark B as sealed, and the performance will be difference.

The reason is: When B is not sealed, it can, in fact, be another sub-class so a virtual call is made. If you seal it, the call to the method will be non-virtual, because it is guaranteed that it will not be virtual.
Maybe it is a small difference. And I am sure there are a lot of optimizations that may render such techniques obsolete (that's why I used SomeNonInlineMethodThatReturnsB, as I think that if you do new B the compiler will notice that B variable has a B instance and will avoid the virtual call [I am not sure]). Also, you may consider the speed difference very insignificant, but there is one. But that's why C# does not consider every method virtual to start with. A class with no virtual methods will really don't have any speed improvement from being sealed.
Also, even the arrays have a problem with non-sealed classes, as you can get an B[] as an A[] if B is a sub-class of A. But each time you want to put a value inside that array it checks to see if it is really a B. Even if you get the array as B[], it may need to check if it is not, in fact, a C[]. If B is sealed, there is no such check. So, sealing the type helps on array accesses too.


If you want to check, this is the result in my computer:

00:00:05.5692125
00:00:05.9787582

From the following code:
using System;
using System.Diagnostics;
 
namespace SealedVsNonSealed
{
  class NonSealed
  {
  }
 
  sealed class Sealed
  {
  }
 
  class Program
  {
    private const int ARRAYSIZE = 10000000;
    private const int COUNT = 100;
    static void Main(string[] args)
    {
      var sw = new Stopwatch();
 
      var sealedInstance = new Sealed();
      var sealedArray = new Sealed[ARRAYSIZE];
      for(int j=0; j<COUNT; j++)
      {
        if (j == 10)
        {
          sw.Reset();
          sw.Start();
        }
 
        for(int i=0; i<ARRAYSIZE; i++)
          sealedArray[i] = sealedInstance;
      }
      sw.Stop();
      Console.WriteLine(sw.Elapsed);
 
      var nonSealedInstance = new NonSealed();
      var nonSealedArray = new NonSealed[ARRAYSIZE];
      for(int j=0; j<COUNT; j++)
      {
        if (j == 10)
        {
          sw.Reset();
          sw.Start();
        }
 
        for(int i=0; i<ARRAYSIZE; i++)
          nonSealedArray[i] = nonSealedInstance;
      }
      sw.Stop();
      Console.WriteLine(sw.Elapsed);
 
      Console.ReadLine();
    }
  }
}
Note: You should compile it in Release mode and execute it outside of visual studio. I tried to make it only start counting when the cpu cache is already on (that's why it executes 10 times before starting to count). The sealed version always gives better result, but the difference is small. You can even invert the order of the sealed and the non-sealed version, and the sealed is always faster.

modified 29-Jun-12 15:41pm.

GeneralMy vote of 5 Pin
AhsanS18-May-12 3:22
memberAhsanS18-May-12 3:22 
GeneralMy vote of 4 Pin
akosidab14-May-12 20:24
memberakosidab14-May-12 20:24 
Questionreally useful info Pin
Rahul Rajat Singh14-Feb-12 8:03
memberRahul Rajat Singh14-Feb-12 8:03 
Question[My vote of 1] performance Pin
radioman.lt19-Oct-11 3:10
memberradioman.lt19-Oct-11 3:10 
AnswerRe: [My vote of 1] performance Pin
George Mamaladze19-Oct-11 5:51
memberGeorge Mamaladze19-Oct-11 5:51 
GeneralRe: [My vote of 1] performance Pin
SAKryukov3-Jun-12 22:55
mvpSAKryukov3-Jun-12 22:55 
AnswerRe: [My vote of 1] performance Pin
SAKryukov3-Jun-12 22:53
mvpSAKryukov3-Jun-12 22:53 
QuestionVoted 5 Pin
zenwalker198514-Oct-11 1:27
memberzenwalker198514-Oct-11 1:27 
GeneralMy vote of 5 Pin
Reiss17-Aug-11 4:30
memberReiss17-Aug-11 4:30 
QuestionGood info, small typo :) Pin
Ant210015-Aug-11 6:38
memberAnt210015-Aug-11 6:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.161128.1 | Last Updated 12 Aug 2011
Article Copyright 2011 by George Mamaladze
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid