9 Key Concepts To Keyword 'Static'

Introduction

When we talk about the Object Oriented Programming (OOP) methodology, our mind strikes with Abstraction, Encapsulation, Polymorphism, and Inheritance. As Coffee blends more people on the table, discussion tends to stretch one level more with data hiding, overloading, overriding, abstract classes, and sealed classes. In my experience, every time I have revisited this jargon, I come across an unknown corner that left me surprised. One such keyword is “Static” It starts with the simple concept of “Sharing,” but in-depth, it leads to some surprising results. Today we will explore these concepts.

Disclaimer. This article covers the concepts of the static keyword with respect to the C# programming language. some of these concepts may not be applicable to other high-level programming languages.

Before we begin

For those of you with little or no idea about the keyword static, I would like to brush up on the concepts.

For example, we have the following DistanceConverter class that has one of the methods called GetDistanceInKM() that accepts the distance in Miles and converts them to KM (Kilometers).

/// <summary>
/// Converts distance from one unit to another
/// </summary>
public class DistanceConverter
{
    private float _multiplier = 1.60934F;
    /// <summary>
    /// Return the distance in KM
    /// </summary>
    /// <param name="distanceInMiles">Distance in Miles</param>
    /// <returns>Distance in KM</returns>
    public float GetDistanceInKM(float distanceInMiles)
    {
        return _multiplier * distanceInMiles;
    }
}

Main() function

using System;
class Program
{
    static void Main()
    {
        DistanceConverter objD1 = new DistanceConverter();
        Console.WriteLine("D1: " + objD1.GetDistanceInKM(2));
        DistanceConverter objD2 = new DistanceConverter();
        Console.WriteLine("D2: " + objD2.GetDistanceInKM(3));
        DistanceConverter objD3 = new DistanceConverter();
        Console.WriteLine("D3: " + objD3.GetDistanceInKM(4));
        Console.ReadLine();
    }
}

As you can see from Main(), we have created multiple objects of the class DistanceConverter. Here is the behind-the-scenes memory allocation.

DistanceConverter

As you can see, objects objD1, objD2, and objD3 are all using the same field and the same method. It's just that they happen to pass various values of distance in miles to get the distance in KM. In a closer look, the value for the field _multiplier is the same. If we were to create another 100, 200, or 1000 objects, the value of the field _multiplier would have been the same repeated all over. But wait, that's a bad use of memory! In a true sense, it would be nice to have 1 copy of _multiplier shared across all the instances of the class. Just as shown in the diagram below.

diagram

Hence the concept, the concept of sharing is referred to as a Static or Shared (in the VB.NET world) field or variable. Similarly, we can have constructors, methods, properties, or even entire classes shared. If we were to do this programmatically, we would use the static keyword as shown below.

/// <summary>
/// Converts distance from one unit to another
/// </summary>
public class DistanceConverter
{
    private static float _multiplier = 1.60934F;
    /// <summary>
    /// Return the distance in KM
    /// </summary>
    /// <param name="distanceInMiles">Distance in Miles</param>
    /// <returns>Distance in KM</returns>
    public float GetDistanceInKM(float distanceInMiles)
    {
        return _multiplier * distanceInMiles;
    }
}

In the example above, the _multiplier is static and hence referred to as a Static member of the class, whereas the GetDistanceInKM() method is non-static and hence referred to as an instance member of the class. With this example, we have tried to see the significance of the keyword static. Let's return to our main discussion and explain the key concept of the keyword static that is worth remembering.

1. Applicability

A class can be static, but a structure and interface cannot be. Similarly, constructors, fields, properties, and methods can be static, but destructors cannot be.

We can have static classes, fields, properties, constructors, and methods.

// Static class
static class StaticClass
{
    // Some class members
}
public class SomeClass
{
    // Static Field
    private static int intStatic = 0;
    // Static Property
    public static string StaticProperty { get; set; }
    // Static Constructor
    static SomeClass()
    {
        // Initialization logic here if needed
    }
    // Static Method
    public static void DoSomething()
    {
        Console.WriteLine(intStatic);
    }
}

But we cannot have static interfaces, structures or destructors.

// Interface with static methods
public interface IStaticInterface
{
    static void SomeStaticMethod()
    {
        // Implementation here
    }
}
// Static class with static members
public static class StaticClass
{
    public static void SomeStaticMethod()
    {
        // Implementation here
    }
}
// Some Random class
class SomeClass1
{
    // Static Destructor cannot be declared in C#
    // static ~SomeClass1() { }
}

If we try to create one, we will get the following compilation error.

” The modifier 'static' is not valid for this item”

Later, we will also explain why such implementation does make sense.

2. Access Modifiers

A static constructor does not have access modifiers. Other static elements do.

We can have static classes, fields, properties, and methods denoted with the access modifier. But if you do so with a static constructor, as in the following.

// Static class with Access Modifier
public class SomeClass
{
    // Static Field with Access Modifier
    public static int IntStatic = 0;
    // Static Property with Access Modifier
    public static string StaticProperty { get; set; }
    // Static Constructor (Note: Constructors cannot be declared static in C#)
    static SomeClass()
    {
        // Initialization logic here if needed
    }
    // Static Method with Access Modifier
    public static void DoSomething()
    {
        Console.WriteLine(IntStatic);
    }
}

It will result in the following compilation error.

”Access modifiers are not allowed on static constructors”

Also, if you notice, so far, I have used the word constructor but not constructor(s). Why? We will see next.

3. Unique Static Constructor

A class can have only one static constructor, and that, too, is parameterless.

If we use overloading, we can have multiple constructors, each having a different signature. If we try to mark them static as in the following.

public class SomeClass
{
    // Static Constructor without parameters
    static SomeClass()
    {
        // Do something
    }
    // Regular Constructor with parameters
    public SomeClass(int input)
    {
        // Do something with the input parameter
    }
}

We will get the compilation error.

"A static constructor must be parameterless”

So, a class cannot have a static constructor with parameter(s), and hence it can have only one static constructor, and that is parameterless.

4. Priority - Static vs Instance Constructor

A static constructor executes well before an instance constructor and only executes once.

Let's create one simple program to prove this.

using System;
public class SampleClass
{
    // Static Constructor
    static SampleClass()
    {
        Console.WriteLine("This is SampleClass Static Constructor.");
    }
    // Instance Constructor
    public SampleClass()
    {
        Console.WriteLine("This is SampleClass Instance Constructor.");
    }
}
class Program
{
    static void Main()
    {
        SampleClass objC1 = new SampleClass();
        SampleClass objC2 = new SampleClass();
        SampleClass objC3 = new SampleClass();
        Console.ReadLine();
    }
}

The output of this program is as follows.

program

As you can see, that static constructor was first to execute and only executes once. We will again revisit this point. Especially the words “well before”.

5. Accessibility

Instance members are accessed with a class object outside the class definition and "this" keyword inside the class definition whereas static members can be directly accessed with a class name outside and inside the definition.

This is an important point and it makes sense also since static members are independent of instance objects.

using System;
public class SampleClass
{
    public string instanceMsg = "This is instance Field.";
    public static string staticMsg = "This is static Field";
    public SampleClass()
    {
        // Access within class definition
        Console.WriteLine("Within class instanceMsg: {0}", this.instanceMsg);
        Console.WriteLine("Within class staticMsg: {0}", SampleClass.staticMsg);
    }
}
class Program
{
    static void Main()
    {
        SampleClass ObjS1 = new SampleClass();
        // Access outside class definition
        Console.WriteLine("From Main Program instanceMsg: {0}", ObjS1.instanceMsg);
        Console.WriteLine("From Main Program staticMsg: {0}", SampleClass.staticMsg);
        Console.ReadLine();
    }
}

We will revisit this in future sections.

6. Compatibility – Static vs Instance members/container

An instance member cannot be created or accessed from a static container, whereas static members can be created or accessed from an instance container.

The subheading is not clear! Don't worry. Let's understand this with some examples.

In Scenario 1, we are trying to access the static field iInc within the instance method DoSomething().

using System;
public class InstanceContainerStaticMember
{
    private static int iInc = 0;
    // Instance Container Method
    public void DoSomething()
    {
        // Static Member
        iInc++;
        Console.WriteLine("iInc: {0}", iInc);
    }
}

On the other hand, in Scenario 2, we tried to access an instance member iInc within the static method DoSomething().

using System;
public class StaticContainerInstanceMember
{
    int iInc = 0;
    /// Static Container Method
    public static void DoSomething()
    {
        // This will result in a compilation error
        // Instance Member cannot be accessed from a static method
        Console.WriteLine("Cannot access instance member iInc from a static method.");
    }
}

But when we compiled, Scenario 2 throws the following compilation error.

“An object reference is required for the non-static field, method, or property”

So the compilation error is clear that we cannot access an instance member from a static container. Why? We will explore this with an example. Recall point 5. According to that, static members are referred to with a class name. So in the example below.

public class SampleClass 
{
    private int _id;
    public SampleClass(int ID)
    {
        _id = ID;
    }
    public static void StaticMethod()
    {
        // I want to access _id but who will provide me
    }
}
class Program 
{
    static void Main() 
    {
        SampleClass s1 = new SampleClass(1);
        SampleClass s2 = new SampleClass(2);
        SampleClass s3 = new SampleClass(3);
        // What will be the value for this s1, s2 or s3
        SampleClass.StaticMethod();
    }
}

We have SampleClass that has a non-static field _id and static method StaticMethod() that wants to access _id. In the Main() program, there are the 3 objects s1, s2, and s3 created of the class SampleClass. And hence the value for _id will be 1, 2, and 3 for the objects s1, s2, and s3, respectively. Now the program makes calls to the SampleClass.StaticMethod(). Since it is called with the class name, if the static method were allowed access to _id, which _id value should it return, s1, s2, or s3? No, it cannot return any since StaticMethod() is independent of the class objects and hence independent of instance members, so the compiler shows the error. This holds true for static classes as well. A static class cannot have an instance member such as fields, properties, constructors, or methods. Try it yourself.

7. Object creation and Instantiation

A static class object can neither be created nor instantiated.

Since a static class cannot have instance members, it cannot be instantiated. In the example below, we are trying to create an instance of the class SampleClass.

public static class SampleClass
{
    // static class members
}
class Program
{
    static void Main()
    {
        // This statement will result in a compilation error.
        // SampleClass s1 = new SampleClass();
    }
}

We get the compilation error.

Cannot declare a variable of static type 'SampleClass' Cannot create an instance of the static class ‘SampleClass'

Important

When building an application, you will encounter code blocks that are repetitively used in the application but don't belong to a specific code hierarchy. Usually, developers refer to them as utility functions. They serve general utility purposes in the application. You certainly want to modularize such code. At the same time, you want to access these functions without any object creation. Static classes are great for organizing these functions. In the Microsoft .NET Framework, one of the great examples of the utility class is the Math class. See the screenshot below.

Math class

8. Inheritance

A static class cannot be part of an inheritance. It cannot serve as a base class, child class or implement an interface.

Yes, that's true. As always, let us see the following examples.

Case 1. A static class cannot be a base class.

public static class BaseClass
{
    // Class members go here
}
public class DeriveClass : BaseClass
{
    // Class members go here
}

“Cannot derive from static class 'BaseClass'”

Case 2. A static class cannot be a derived class.

public class BaseClass
{
    // Class members go here
}
public static class DeriveClass : BaseClass
{
    // Class members go here
}

“ Static class 'DeriveClass' cannot derive from type 'BaseClass'. Static classes must derive from the object. ”.

Case 3. A static class cannot implement interfaces.

public interface IInterface
{
    // Interface members go here
}
public static class DeriveClass : IInterface
{
    // Class members go here
}

“'DeriveClass' static classes cannot implement interfaces”

That's clear except for one question. As we have seen, if a static class cannot be a part of an inheritance hierarchy, then what will happen to protected members in the static class? The answer is a static class cannot have protected members. If you try to declare one, it will throw.

public class BaseClass
{
    public static virtual void StaticMethod()
    {
        // Base class static method implementation
    }
}
public class DerivedClass : BaseClass
{
    public override void StaticMethod()
    {
        // Derived class static method implementation
    }
}

Compilation error.

“ Static classes cannot contain protected members ”

In fact, static members in non-static classes cannot be overridden.

public class BaseClass
{
    public static virtual void StaticMethod()
    {
        // Base class static method implementation
    }
}
public class DerivedClass : BaseClass
{
    public override void StaticMethod()
    {
        // Derived class static method implementation
    }
}

Compilation error.

“ A static member cannot be marked as override, virtual, or abstract ”

9. Lifetime

Static elements are in scope as soon as they are referred to in a program for the first time and will remain in scope throughout the life of the AppDomain.

This is an important concept to understand the scope of static elements but I will explain something more than that. Have a look at the example below.

using System;
public class SampleClass
{
    private static int _iCount = 0;
    static SampleClass()
    {
        Console.WriteLine("This is Static Ctor");
    }
    public void SetValue(int Count)
    {
        _iCount = Count;
    }
    public static void Print()
    {
        Console.WriteLine("The value of Count: {0}", SampleClass._iCount);
    }
}

In the class above, we have a static field _iCount and static method Print(). In the Main() program below, we are making a call to the functions function1() and function2(). In both of the functions, we are creating an object of the class SampleClass.

using System;
public class SampleClass
{
    private static int _iCount = 0;

    static SampleClass()
    {
        Console.WriteLine("This is Static Ctor");
    }

    public void SetValue(int Count)
    {
        _iCount = Count;
    }
    public static void Print()
    {
        Console.WriteLine("The value of Count: {0}", SampleClass._iCount);
    }
}
class Program
{
    static void Main()
    {
        Console.WriteLine("First line in Main().");
        SampleClass.Print();
        function1();
        function2();
        SampleClass.Print();
        Console.WriteLine("Last line in Main().");
        Console.ReadLine();
    }
    public static void function1()
    {
        Console.WriteLine("First line in function1().");
        SampleClass objS2 = new SampleClass();
        objS2.SetValue(1);
        Console.WriteLine("Last line in function1().");
    }
    public static void function2()
    {
        Console.WriteLine("First line in function2().");
        SampleClass objS3 = new SampleClass();
        objS3.SetValue(2);
        Console.WriteLine("Last line in function2().");
    }
}

And finally the following is the output of this program.

output of this program

A couple of interesting observations.

  • The second line of output is the call to the static constructor (see “This is static Ctor”). But if you refer to the Main() function, we are neither creating the object nor instantiating it. Even SampleClass is not declared as static. Yet, the program makes a static constructor call. Remember in Point 4, we discussed that a static constructor is called well before the instance constructor. The word “well before” is important since it suggests that as soon as static elements are accessed (with or without object creation), the constructor will initialize and then the static objects will be in scope but not as soon as the program begins executing. Because the first line in the output (in other words, “First line in Main().”) is the Main() function Console. write line () statement, and then the constructor of SampleClass is called. To conclude, static elements are in scope as soon as they are referred to by the program in any way.
  • Also, there are the objects ObjS2 and ObjS3 created in function1() and function2() respectively. Ideally, their scope is within the respective function body only. But when we print the value of the _iCount in Main(), the printed value “2” is set by function2() (in other words objS3.SetValue(2)).

So the takeaway from the last point is that a static element remains active until the last line of the program is executed. Our assumption is absolutely right with respect to this program. The reason is, that the scope of our program is limited to one AppDomain. However, if we introduce a different AppDomain, things will change. Let's see another example.

Assume we have one class library project.

using System;
namespace RemoteClassLibrary
{
    public class RemoteClass
    {
        private static int _myID = 0;
        public RemoteClass()
        {
            Console.WriteLine("My ID is {0}", ++_myID);
        }
    }
}

And we have saved the class library DLL to the local drive c:\app\. We have also created another program that uses reflection to execute this code.

using System;
using System.Reflection;
namespace TestingProgram
{
    class Program
    {
        static void Main()
        {
            const string ClassLibraryPath = @"c:\app\RemoteClassLibrary.dll";
            // Load assembly in current App Domain
            Assembly Assembly1 = Assembly.LoadFrom(ClassLibraryPath);
            Assembly1.CreateInstance("RemoteClassLibrary.RemoteClass");
            // Load assembly in current App Domain
            Assembly Assembly2 = Assembly.LoadFrom(ClassLibraryPath);
            Assembly2.CreateInstance("RemoteClassLibrary.RemoteClass");
            Console.ReadKey();
        }
    }
}

And the obvious output of this program is.

obvious output

Please note that even though that assembly is loaded, static members still persist the value, or you can say only a single copy of the static class is created.

Now let's modify the main program to introduce multiple AppDomains that will load this assembly separately.

using System;
class Program
{
    static void Main()
    {
        const string ClassLibrary = "RemoteClassLibrary.RemoteClass";
        const string ClassLibraryPath = @"c:\app\RemoteClassLibrary.dll";
        // Load class Library in AppDomain1
        AppDomain AppDomain1 = AppDomain.CreateDomain("AppDomain1");
        AppDomain1.CreateInstanceFrom(ClassLibraryPath, ClassLibrary);
        // Load class Library in AppDomain2
        AppDomain AppDomain2 = AppDomain.CreateDomain("AppDomain2");
        AppDomain2.CreateInstanceFrom(ClassLibraryPath, ClassLibrary);
        Console.ReadKey();
    }
}

Surprisingly the output.

output

And so, even though a single program is executing, the different AppDomain is causing the program to maintain 2 copies of the static member per AppDomain. In short, their scope is limited to the AppDomain.

On a final note, we explained that the static elements are in scope as soon as they are accessed first and limited to a specific AppDomain.

Why interfaces, structures, and destructors cannot be static?

I've said in Point 1 that interfaces, structures, and destructors cannot be static, and I intentionally left the discussion without adding more details because we need to have enough of a base before we can explain that. Now that we've visited some of the key concepts of static members, this is a mature point to explain in detail.

An interface sets up guidelines and expectations for the implementing classes of what their capabilities should be. If a class is a Blueprint for the object, then an interface provides outlines/contracts for the classes. To justify, interfaces describe an object's capabilities but don't provide the object representation.

To make it clearer, let's see an example.

using System;
public interface IVehicle
{
    void Drive();
}
public class Car : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("I am Driving Car");
    }
}
public class Bike : IVehicle
{
    public void Drive()
    {
        Console.WriteLine("I am Riding Bike");
    }
}

As you can see in the example above, the interface IVehicle provides a contract for Classes Car and Bike. And Car and Bike provide their native representation. IVehicle doesn't have its own representation but can ask Car and Bike to provide their own representation.

using System;
class Program
{
    static void Main()
    {
        IVehicle vehicle1 = new Car();
        vehicle1.Drive();
        // I am Driving Car
        IVehicle vehicle2 = new Bike();
        vehicle2.Drive();
        // I am Riding Bike
        Console.Read();
    }
}

Now for the sake of understanding, even if we assume that the IVehicle is allowed to have a static something, it would not have an object representation since a static type cannot be instantiated (remember Point 7) and IVehicle.Drive() will not have the representation. Thus semantically, a static interface doesn't make sense.

Coming to structures, structures are separate from the class with respect to the way they are represented in memory. Structures are value types, and classes are reference types. Since we cannot have an instance of a static type, the result of a static class and static struct would be the same representation. It will cause unnecessary repetition and confusion.

Finally, destructors are intended for object memory cleanup (to free up resources). Static types don't have an object representation so we don't need static destructors, just like structures (value types) don't have destructors.

Final Words

We began with the simple concept of sharing the variables across all the objects of a class, but then we made a deep dive into each scenario that this simple concept results in, and that certainly left us by surprise. To provide you with a more precise example, it's just like a client is proposing to implement a simple program, but then over the period when you build, test, revisit, and deliver, you will realize how much detail is required to finish the simple program. In our entire discussion, I have tried to show everything with an example to make it short, simple and yet effective. If you have anything that promises to change the article title from “9 Key Concepts” to 10, 11, 12, or even more, let me know. I will be more than happy to add your suggestions, input, and comments.

Happy Learning.

Version History

  • Version 1.0: Initial article submitted.
  • Version 1.1: The following Changes.Added "Why Interface, structure and destructor cannot be static?".
  • Point 8: Added Explanation for "Protected member of class" along with "Virtual" and "Override".
  • Point 9: Elaborate scope of "Static element scope" to "AppDomain".


Similar Articles