65.9K
CodeProject is changing. Read more.
Home

Introducing C# 2.0 static classes

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.10/5 (18 votes)

Oct 3, 2005

CPOL

2 min read

viewsIcon

100081

A general discussion on C# 2.0 static classes.

Introduction

The use of static classes should not be confused with the common practice in Java to represent an inner class as a nested top-level class in order to conveniently group related classes without creating a new package. Rather, it provides a mechanism to explicitly declare a class as not instantiable. Creating a class that is not intended to be instantiated and which contains only static members has historically been accomplished by declaring it as sealed with a private constructor, as specified in the example below:

public sealed class BluetoothUART
{
    int broadcastBand;
    
    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);

    private BluetoothUART()
    {
    }

    public int BroadCastBand
    {
      get
      {
          return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
          return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

The example above declares a sealed class BluetoothUART with a private constructor and a static property DeviceID. Attempting to instantiate this class will result in a compile time error.

BluetoothUART blu = new BluetoothUART(); //wont work
Console.WriteLine(BluetoothUART.DeviceID);  //works

Using this approach will prevent instantiation and subclassing of the given class, but it neither prevents the use of the class as the type of a variable or parameter, nor does it prevent the class from declaring instance members. The example below illustrates the shortcomings of the above strategy:

public sealed class BluetoothUART
{
    int broadcastBand;

    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);

    private BluetoothUART()
    {
    }

    public int BroadCastBand
    {
      get
      {
        return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
        return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

class Program
{
    static void Main(string[] args)
    {
      BluetoothUART buart = null;
      Console.WriteLine(BluetoothUART.DeviceID);  //works
    }
}

As you can see from the example, although defining BluetoothUART is sealed with a private constructor, it can still be used as the type of the variable buart; a useless exercise since it can only be null, and can still define instance properties, methods, and fields, which will, of course, be inaccessible since no instance will ever exist to access them through. C# 2.0 introduces the concept of static classes as a means to formalize the underlying design pattern and provide much stronger checking of the restrictions that are logically associated with it. The static construct can be utilized to rewrite the BluetoothUART class defined above, as follows:

public static class BluetoothUART
{
    int broadcastBand;
    
    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);
    

    public int BroadCastBand
    {
      get
      {
        return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
        return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

In the example above, the sealed keyword has been replaced with static, and the private constructor has been removed as it is unnecessary. Attempting to build this class will generate a compile time error since the instance field and property have not been removed. This serves as evidence of the tighter restrictions enforced when the class is defined as static rather than sealed with a private constructor. A version of the BluetoothUART class that will compile is given below:

public static class BluetoothUART
{
    static int broadcastBand = 0;
    
    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);
    
    public static int BroadCastBand
    {
      get
      {
        return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
        return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

Given the sample above, you will also find that the consuming class definition defined below will fail to compile.

class Program
{
    static void Main(string[] args)
    {
      BluetoothUART buart = null; //fails to compile
      Console.WriteLine(BluetoothUART.DeviceID);  //works
    }
}

Again, this is due to the stronger restrictions applied to static classes which do not permit instance fields or for types of that class to be used as a variable's type. Likewise, a static class cannot be defined as sealed or abstract, so neither class definition below will compile.

//sealed will not compile
public sealed static class BluetoothUART
{
    static int broadcastBand = 0;

    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);
    
    public static int BroadCastBand
    {
      get
      {
        return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
        Return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

//abstract will not compile
public abstract static class BluetoothUART
{
    static int broadcastBand = 0;

    [DllImport("user32.dll")]
    static extern int DeviceIOControl(string guid);
    
    public static int BroadCastBand
    {
      get
      {
        return this.broadcastBand;
      }
    }

    public static int DeviceID
    {
      get
      {
        return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
      }
    }
}

Another restriction on static classes prevents us from defining parent classes or defining interfaces for it, so the example defined below will also not compile:

public static class BluetoothUART : System.StringComparer
{
   static int broadcastBand = 0;

   [DllImport("user32.dll")]
   static extern int DeviceIOControl(string guid);

   public static int BroadCastBand
   {
       get
       {
          return this.broadcastBand;
       }
   }

   public static int DeviceID
   {
       get
       {
          return DeviceIOControl("49ECA277-65F7-446b-9206-4C09580DDD88");
       }
   }
}