Static


The HelloWorld example in Chapter 1 first presented the keyword static; however, it did not define it fully. This section defines the static keyword fully.

Language Contrast: C++/Visual BasicGlobal Variables and Functions

Unlike many of the languages that came before it, C# does not have global variables or global functions. All fields and methods in C# appear within the context of a class. The equivalent of a global field or function within the realm of C# is a static field or function. There is no functional difference between global variables/functions and C# static fields/methods, except that static fields/functions can include access modifiers, such as private, that can limit the access and provide better encapsulation.


To begin, consider an example. Assume that the employee Id value needs to be unique for each employee. One way to accomplish this is to store a counter to track each employee ID. If the value is stored as an instance field, however, every time you instantiate an object, a new NextId field will be created such that every instance of the Employee object would consume memory for that field. The biggest problem is that each time an Employee object instantiated, the NextId value on all of the previously instantiated Employee objects would need to be updated with the next ID value. What you need is a single field that all Employee object instances share.

Static Fields

To define data that is available across multiple instances, you use the static keyword, as demonstrated in Listing 5.22.

Listing 5.22. Declaring a Static Field

 class Employee {   public Employee(string firstName, string lastName)   {       FirstName = firstName;       LastName = lastName;       Id = NextId;                                        NextId++;                                       }   // ...   public static int NextId;                             public int Id;   public string FirstName;   public string LastName;   public string Salary;   // ... } 

In this example, the NextId field declaration includes the static modifier and therefore is called a static field. Unlike Id, a single storage location for NextId is shared across all instances of Employee. Inside the Employee constructor, you assign the new Employee object's Id the value of NextId immediately before incrementing it. When another Employee class is created, NextId will be incremented and the new Employee object's Id field will hold a different value.

Just as instance fields (nonstatic fields) can be initialized at declaration time, so can static fields, as demonstrated in Listing 5.23.

Listing 5.23. Assigning a Static Field at Declaration

 class Employee {   // ...   public static int NextId = 42;                          // ... } 

If no initialization for a static field is provided, the static field will automatically be assigned its default value (0, null, false, and so on), and it will be possible to access the static field even if it has never been explicitly assigned.

Nonstatic fields, or instance fields, have a new value for each object to which they belong. In contrast, static fields don't belong to the instance, but rather, to the class itself. As a result, you access a static field from outside a class via the class name. Consider the new Program class shown in Listing 5.24.

Listing 5.24. Accessing a Static Field

 class Program {   static void Main()   {       Employee.NextId = 1000000;                                         Employee employee1 = new Employee(            "Inigo", "Montoya");       Employee employee2 = new Employee(           "Princess", "Buttercup");       Console.WriteLine(           "{0} {1} ({2})",           employee1.FirstName,           employee1.LastName,           employee1.Id);       Console.WriteLine(           "{0} {1} ({2})",           employee2.FirstName,           employee2.LastName,           employee2.Id);       Console.WriteLine("NextId = {0}", Employee.NextId);             }    // ... } 

Output 5.4 shows the results of Listing 5.24.

Output 5.4.

 Inigo Montoya (1000000) Princess Buttercup (1000001) NextId = 1000002 

To set and retrieve the initial value of the NextId static field, you use the class name, Employee, not a variable name. The only time you can eliminate the class name is from within code that appears within the class itself. In other words, the Employee(...) constructor did not need to use Employee.NextId because the code appeared within the context of the Employee class itself, and therefore, the context was already understood from the scope.

Even though you refer to static fields slightly differently than instance fields, it is not possible to define a static and an instance field with the same name in the same class. The possibility of mistakenly referring to the wrong field is high, and therefore, the C# designers decided to prevent such code.

Beginner Topic: Data Can Be Associated with Both a Class and an Object

Both classes and objects can have associated data, just as can the molds and the widgets created from them.

For example, a mold could have data corresponding to the number of widgets it created, the serial number of the next widget, the current color of the plastic injected into the mold, and the number of widgets it produces per hour. Similarly, a widget has its own serial number, its own color, and perhaps the date and time when the widget was created. Although the color of the widget corresponds to the color of the plastic within the mold at the time the widget was created, it obviously does not contain data corresponding to the color of the plastic currently in the mold, or the serial number of the next widget to be produced.

In designing objects, programmers should take care to declare both fields and methods appropriately as static or instance based. In general, you should declare methods that don't access any instance data as static methods, and methods that access instance data (where the instance is not passed in as a parameter) as instance methods. Static fields store data corresponding to the class, such as defaults for new instances or the number of instances that have been created. Instance fields store data associated with the object.


Static Methods

Just like static fields, you access static methods directly off the class name (Console.ReadLine(), for example). Furthermore, it is not necessary to have an instance in order to access the method.

Because static methods are not referenced through a particular instance, the this keyword is invalid inside a static method. In fact, it is not possible to access either an instance field or an instance method directly from within a static method without a reference to the particular instance to which the field or method belongs.

Static Constructors

In addition to static fields and methods, C# also supports static constructors. Static constructors are provided as a means to initialize a class (not the class instance). Static constructors are not called explicitly; instead, the runtime calls static constructors automatically upon first access to the class, whether via calling a regular constructor or accessing a static method or field on the class. You use static constructors to initialize the static data within the class to a particular value, particularly when the initial value involves more complexity than a simple assignment at declaration time. Consider Listing 5.25.

Listing 5.25. Declaring a Static Constructor

 class Employee {   static Employee()   {       Random randomGenerator = new Random();       NextId = randomGenerator.Next(101, 999);    }   // ...   public static int NextId   {       get       {           return _NextId;       }       private set       {          _NextId = value;       }   }   public static int _NextId = 42;   // ... } 

Listing 5.25 assigns the initial value of NextId to be a random integer between 100 and 1,000. Because the initial value involves a method call, the NextId initialization code appears within a static constructor and not as part of the declaration.

If assignment of NextId occurs within both the static constructor and the declaration, it is not obvious what the value will be when initialization concludes. The C# compiler generates CIL in which the declaration assignment is moved to be the first statement within the static constructor. Therefore, NextId will contain the value returned by randomGenerator.Next(101, 999) instead of a value assigned during NextId's declaration. Assignments within the static constructor, therefore, will take precedence over assignments that occur as part of the field declaration, as was the case with instance fields. Note that there is no support for defining a static finalizer.

Static Classes

Some classes do not contain any instance fields. Consider, for example, a Math class that has functions corresponding to the mathematical operations Max() and Min(), as shown in Listing 5.26.

Listing 5.26. Declaring a Static Class

 // Static class introduced in C# 2.0 public static class SimpleMath                               {    // params allows the number of parameters to vary.    static int Max(params int[] numbers)    {         // Check that there is a least one item in numbers.         // ...         int result;         result = numbers[0];         foreach(int number in numbers)         {             if(number > result)             {                 result = number;             }         }         return result;    }    // params allows the number of parameters to vary.    static int Min(params int[] numbers)    {         // Check that there is a least one item in numbers.         // ...         int result;         result = numbers[0];         foreach(int number in numbers)         {             if(number < result)             {                 result = number;             }         }         return result;    }  } 

This class does not have any instance fields (or methods), and therefore, creation of such a class would be pointless. Because of this, the class is decorated with the static keyword. The static keyword on a class provides two facilities. First, it prevents a programmer from writing code that instantiates the SimpleMath class. Second, it prevents the declaration of any instance fields or methods within the class. Since the class cannot be instantiated, instance members would be pointless.

C# 1.0 did not support static class declaration like this. Instead, programmers had to declare a private constructor. The private constructor prevented developers from ever instantiating an instance of the class outside of the class scope. Listing 5.27 shows the same Math class using a private constructor.

Listing 5.27. Declaring a Private Constructor

 // Preventing instantiation in C# 1.0 with a private constructor. class SimpleMath {    private SimpleMath() {}                                  // params allows the number of parameters to vary.   static int Max(params int[] numbers)   {        // ...   }   // params allows the number of parameters to vary.   static int Min(params int[] numbers)   {        // ...   } } 

The effect of using a private constructor in Listing 5.27 is very similar to the static class used in Listing 5.26, except that it is still possible to instantiate the class in Listing 5.27 from inside the class implementation. In contrast, Listing 5.26 prevents instantiation from anywhere, including from inside the class itself. Another difference between declaring a static class and using a private constructor is that instance members are allowed on a class with private constructors, but the C# 2.0 compiler will disallow any instance members on a static class.

One more distinguishing characteristic of the static class is that the C# compiler automatically marks it as sealed. This keyword designates the class as inextensible; in other words, no class can be derived from it.




Essential C# 2.0
Essential C# 2.0
ISBN: 0321150775
EAN: 2147483647
Year: 2007
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net