static Keyword 2020
A C# class can define any number of static members using
The static members can be invoked directly from the class level rather than a type instance. Let's check how they are different.
Console c;
If we write the code, we get an error message: Cannot declare a variable of static type System.Console.
We cannot invoke the WriteLine() method from the object level, either.
Console c = new Console(); c.WriteLine("This is an error");
The right invocation of the method WriteLine() is:
Console.WriteLine("This is the correct use of WriteLine()");
Static members are items that are deemed to be so commonplace that there is no need to create an instance of the type when invoking the member. While any class can define static members, they are most commonly found within utility classes such as System.Console, System.Math, System.Environment, or System.GC, and so on.
Let's take a look at the following example.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StaticMethod { class SavingsAccount { public static Random rnd = new Random(); public static int GetRandomNumber(short uLimit) { return rnd.Next(uLimit); } public static string MSG() { string[] messages = { "msg 1", "msg 2", "msg 3", "msg 4" }; return messages[GetRandomNumber(4)]; } static void Main(string[] args) { for (int i = 0; i < 4; i++) Console.WriteLine(SavingsAccount.MSG()); Console.WriteLine(); } } }
Output we get:
msg 3 msg 3 msg 1 msg 2
The static method MSG() defined in a SavingsAccount class returns a random string, obtained by calling a static helper function GetRandomNumber(). Note that the System.Random member variable and the GetRandomNumber() helper function method have also been declared as static members of the SavingsAccount class, given the rule that static members can operate only on other static members.
If we attempt to use nonstatic class data or call a nonstatic method of the class within a static member's implementation, we'll get compile-time errors.
Besides static members, a type may also define static field data such as the Random member variable rnd in the previous SavingsAccount class. When a class defines nonstatic data (instance data), each object of this type maintains an independent copy of the field. Let's look at the following class.
class SavingsAccount { public double Balance; public SavingsAccount(double b) { Balance = b; } }
When we create SavingsAccount objects, memory for the Balance field is allocated for each class instance. Static data, on the other hand, is allocated once and shared among all objects of the same type. To demonstrate the usefulness of static data, add a static point of data named Rate to the SavingsAccount class, which is set to a default value of 0.05:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StaticMethod { class SavingsAccount { public double Balance; public static double Rate = 0.05; public SavingsAccount(double b) { Balance = b; } public static void SetRate(double r) { Rate = r; } public static double GetRate() { return Rate; } static void Main(string[] args) { SavingsAccount sa1 = new SavingsAccount(100); SavingsAccount sa2 = new SavingsAccount(300); Console.WriteLine("1: Rate = {0}", SavingsAccount.GetRate()); SavingsAccount sa3 = new SavingsAccount(505.25); Console.WriteLine("2: Rate = {0}", SavingsAccount.GetRate()); Console.ReadLine(); } } }
Output we get:
1: Rate = 0.05 2: Rate = 0.05
When we create new instances of the SavingsAccount class, the value of the static data is not reset, as the CLR will allocate the data into memory exactly one time. After that point, all objects of type SavingsAccount operate on the same value.
As mentioned before, static methods can operate only on static data. A nonstatic method, however, can use both static and nonstatic data. This makes sense because static data is available to all instances of the type. To show this, let's update the SavingsAccount.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StaticMethod { class SavingsAccount { public double Balance; public static double Rate = 0.05; public SavingsAccount(double b) { Balance = b; } public static double GetRate() { return Rate; } public static void SetRateObj(double r) { Rate = r; } public double GetRateObj() { return Rate; } static void Main(string[] args) { SavingsAccount.SetRateObj(0.07); SavingsAccount sa1 = new SavingsAccount(100); SavingsAccount sa2 = new SavingsAccount(300); Console.WriteLine("1: Rate = {0}", sa1.GetRateObj()); Console.WriteLine("2: Rate = {0}", sa2.GetRateObj()); Console.WriteLine("3: Rate = {0}", SavingsAccount.GetRate()); Console.ReadLine(); } } }
We get the output:
1: Rate = 0.07 2: Rate = 0.07 3: Rate = 0.07
As expected, the value 0.07 is returned regardless of which SavingsAccount object we ask including asking via the static GetRate() method.
The constructors are used to set the value of a type's data at the time of creation. So, if we were to assign a value to a static data member within an instance-level constructor, we may be surprised to find that the value is reset each time we create a new object. For example, look at the following update of SavingsAccount:
class SavingsAccount { public double Balance; public static double Rate; public SavingsAccount(double b) { Balance = b; Rate += 0.01; } ... }
The updated code looks like this:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StaticMethod { class SavingsAccount { public double Balance; public static double Rate; public SavingsAccount(double b) { Balance = b; Rate += 0.01; } public static void SetRateObj(double r) { Rate = r; } public static double GetRate() { return Rate; } public double GetRateObj() { return Rate; } static void Main(string[] args) { SavingsAccount.SetRateObj(0.03); SavingsAccount sa1 = new SavingsAccount(100); Console.WriteLine("1: Rate = {0}", sa1.GetRateObj()); SavingsAccount sa2 = new SavingsAccount(200); Console.WriteLine("2: Rate = {0}", sa2.GetRateObj()); SavingsAccount sa3 = new SavingsAccount(300); Console.WriteLine("3: Rate = {0}", SavingsAccount.GetRate()); Console.ReadLine(); } } }
If we run the code, we get:
1: Rate = 0.04 2: Rate = 0.05 3: Rate = 0.06
Note that the Rate variable is reset each time we create a new SavingsAccount object.
While we are always can establish the initial value of static data using the member initialization syntax, what if the value for our static data needed to be retrieved from a database or external file? To perform such tasks requires scope to execute the code. For this reason, C# has a static constructor.
static SavingsAccount() { Console.WriteLine("static ctor"); Rate = 0.10; }
A static constructor is a special constructor that is an ideal place to initialize the values of static data when the value is now known at compile time such cases when we need to read in the value from an external file or generate a random number, and so on. Here are a few things related to the static constructors:
- A given class (or structure) may define only a single static constructor.
- A static constructor does not take an access modifier and cannot take any parameters.
- A static constructor executes exactly one time, regardless of how many objects of the type are created.
- The runtime invokes the static constructor when it creates an instance of the class or before accessing the first static member invoked by the caller.
- The static constructor executes before any instance-level constructors.
When a class has been defined as static, it is not creatable using the new keyword, and it can contain only members or fields market with the static keyword.
The class that cannot be created does not appear all that useful. However, if we create a class that contains nothing but static members and constant data, the class has no need to be allocated in the first place. Let's take a look at the following example:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace StaticClass { static class TimeUtilClass { public static void PrintTime() { Console.WriteLine(DateTime.Now.ToShortTimeString()); } public static void PrintDate() { Console.WriteLine(DateTime.Today.ToShortTimeString()); } static void Main(string[] args) { TimeUtilClass.PrintDate(); // Compile error because we can't create static classes! TimeUtilClass u = new TimeUtilClass(); } } }
Because this class has been defined with the static keyword, we cannot create an instance of TimeUtilClass using the new keyword.
Prior to .NET 2.0, the only way to prevent the creation of a class type was to either redefine the default constructor as private or mark the class as an abstract type using the abstract keyword:
class TimeUtilClass { private TimeUtilClass(){} public static void PrintTime() { Console.WriteLine(DateTime.Now.ToShortTimeString()); } public static void PrintDate() { Console.WriteLine(DateTime.Today.ToShortTimeString()); } } abstract class TimeUtilClass { public static void PrintTime() { Console.WriteLine(DateTime.Now.ToShortTimeString()); } public static void PrintDate() { Console.WriteLine(DateTime.Today.ToShortTimeString()); } }
Though these constructs are still permissible, the use of static classes is a cleaner solution and more type-safe, given that the previous two allowed nonstatic members to appear within the class definition without error.
A project's application class which defines the Main() method is often defined as a static class, to ensure it only contains static members and cannot be directly created:
static class Program { static void Main(string[] args) { ... } }
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization