static Class Members
Every object has its own copy of all the instance variables of the class. In certain cases, only one copy of a particular variable should be shared by all objects of a class. A static variable is used in such cases. A static variable represents classwide informationall objects of the class share the same piece of data. The declaration of a static variable begins with the keyword static.
Let's motivate static data with an example. Suppose that we have a video game with Martians and other space creatures. Each Martian tends to be brave and willing to attack other space creatures when it is aware that there are at least four other Martians present. If fewer than five Martians are present, each Martian becomes cowardly. Thus each Martian needs to know the martianCount. We could endow class Martian with martianCount as an instance variable. If we do this, every Martian will have a separate copy of the instance variable, and every time we create a new Martian, we will have to update the instance variable martianCount in every Martian. This wastes space on redundant copies, wastes time updating the separate copies and is error prone. Instead, we declare martianCount to be static, making martianCount classwide data. Every Martian can access the martianCount as if it were an instance variable of class Martian, but only one copy of the static martianCount is maintained. This saves space. We save time by having the Martian constructor increment the static martianCountthere is only one copy, so we do not have to increment separate copies of martianCount for each Martian object.
|
The scope of a static variable is the body of its class. A class's public static members can be accessed by qualifying the member name with the class name and the dot (.) operator, as in Math.PI. A class's private static class members can be accessed only through the methods and properties of the class. Actually, static class members exist even when no objects of the class existthey are available as soon as the class is loaded into memory at execution time. To access a private static member from outside its class, a public static method or property can be provided.
|
|
Our next application declares two classesEmployee (Fig. 9.12) and EmployeeTest (Fig. 9.13). Class Employee declares private static variable count (Fig. 9.12, line 10), and public static property Count (lines 5258). We omit the set accessor of property Count to make the property read-onlywe do not want clients of the class to be able to modify count. The static variable count is initialized to 0 in line 10. If a static variable is not initialized, the compiler assigns a default value to the variablein this case 0, the default value for type int. Variable count maintains a count of the number of objects of class Employee currently in memory. This includes objects that are already inaccessible from the application, but have not yet had their destructors invoked by the garbage collector.
Figure 9.12. static variable used to maintain a count of the number of Employee objects in memory.
(This item is displayed on pages 432 - 433 in the print version)
1 // Fig. 9.12: Employee.cs 2 // Static variable used to maintain a count of the number of 3 // Employee objects in memory. 4 using System; 5 6 public class Employee 7 { 8 private string firstName; 9 private string lastName; 10 private static int count = 0; // number of objects in memory 11 12 // initialize employee, add 1 to static count and 13 // output string indicating that constructor was called 14 public Employee( string first, string last ) 15 { 16 firstName = first; 17 lastName = last; 18 count++; // increment static count of employees 19 Console.WriteLine( "Employee constructor: {0} {1}; count = {2}", 20 FirstName, LastName, Count ); 21 } // end Employee constructor 22 23 // subtract 1 from static count when the garbage collector 24 // calls destructor to clean up object; 25 // confirm that destructor was called 26 ~Employee() 27 { 28 count--; // decrement static count of employees 29 Console.WriteLine( "Employee destructor: {0} {1}; count = {2}", 30 FirstName, LastName, Count ); 31 } // end destructor 32 33 // read-only property that gets the first name 34 public string FirstName 35 { 36 get 37 { 38 return firstName; 39 } // end get 40 } // end property FirstName 41 42 // read-only property that gets the last name 43 public string LastName 44 { 45 get 46 { 47 return lastName; 48 } // end get 49 } // end property LastName 50 51 // read-only property that gets the employee count 52 public static int Count 53 { 54 get 55 { 56 return count; 57 } // end get 58 } // end property Count 59 } // end class Employee |
When Employee objects exist, member count can be used in any method of an Employee objectthis example increments count in the constructor (line 18) and decrements it in the destructor (line 28). When no objects of class Employee exist, member count can still be referenced, but only through a call to public static property Count (lines 5258), as in Employee.Count, which evaluates to the number of Employee objects currently in memory.
Note that the Employee class has a destructor (lines 2631). This destructor is included to decrement static variable count, then show when the garbage collector executes in this application. Unlike constructors and methods, the destructor cannot be invoked explicitly by any programmer-written code. It can only be invoked by the garbage collector, so it does not need an access modifierin fact, it is a syntax error to include one.
EmployeeTest method Main (Fig. 9.13) instantiates two Employee objects (lines 1415). When each Employee object's constructor is invoked, lines 1617 of Fig. 9.12 assign the Employee's first name and last name to instance variables firstName and lastName. Note that these two statements do not make copies of the original string arguments. Actually, string objects in C# are immutablethey cannot be modified after they are created. Therefore, it is safe to have many references to one string object. This is not normally the case for objects of most other classes in C#. If string objects are immutable, you might wonder why we are able to use operators + and += to concatenate string objects. String concatenation operations actually result in a new string object containing the concatenated values. The original string objects are not modified.
Figure 9.13. static member demonstration.
1 // Fig. 9.13: EmployeeTest.cs 2 // Static member demonstration. 3 using System; 4 5 public class EmployeeTest 6 { 7 public static void Main( string[] args ) 8 { 9 // show that count is 0 before creating Employees 10 Console.WriteLine( "Employees before instantiation: {0}", 11 Employee.Count ); 12 13 // create two Employees; count should become 2 14 Employee e1 = new Employee( "Susan", "Baker" ); 15 Employee e2 = new Employee( "Bob", "Blue" ); 16 17 // show that count is 2 after creating two Employees 18 Console.WriteLine( " Employees after instantiation: {0}", 19 Employee.Count ); 20 21 // get names of Employees 22 Console.WriteLine( " Employee 1: {0} {1} Employee 2: {2} {3} ", 23 e1.FirstName, e1.LastName, 24 e2.FirstName, e2.LastName ); 25 26 // in this example, there is only one reference to each Employee, 27 // so the following statements cause the CLR to mark each 28 // Employee object as being eligible for destruction 29 e1 = null; // object e1 no longer needed 30 e2 = null; // object e2 no longer needed 31 32 GC.Collect(); // ask for garbage collection to occur now 33 // wait until the destructors 34 // finish writing to the console 35 GC.WaitForPendingFinalizers(); 36 37 // show Employee count after calling garbage collector and 38 // waiting for all destructors to finish 39 Console.WriteLine( " Employees after destruction: {0}", 40 Employee.Count ); 41 } // end Main 42 } // end class EmployeeTest
|
When Main has finished using the two Employee objects, references e1 and e2 are set to null at lines 2930, so they no longer refer to the objects that were instantiated on lines 1415. The objects become "eligible for garbage collection" because there are no more references to them in the application.
Eventually, the garbage collector might reclaim the memory for these objects (or the operating system will reclaim the memory when the application terminates). C# does not guarantee when, or even whether, the garbage collector will execute, so in line 32, this application explicitly calls the garbage collector using static method Collect of class GC to indicate that the garbage collector should make a "best-effort" attempt to reclaim objects that are inaccessible. This is just a best effortit is possible that no objects or only a subset of the eligible objects will be collected. When method Collect returns, this does not indicate that the garbage collector has finished searching for memory to reclaim. The garbage collector may still, in fact, be executing. For this reason, we call static method WaitForPendingFinalizers of class GC. If the garbage collector has marked any objects eligible for destruction, invoking WaitforPendingFinalizers in line 35 stops the execution of the Main method until the destructors of these objects have been completely executed.
In Fig. 9.13's sample output, the garbage collector did reclaim the objects formerly referenced by e1 and e2 before lines 3940 displayed the current Employee count. The last output line indicates that the number of Employee objects in memory is 0 after the call to GC.Collect(). The third- and second-to-last lines of the output show that the Employee object for Bob Blue was destructed before the Employee object for Susan Baker. The output on your system may differ, because the garbage collector is not guaranteed to execute when GC.Collect() is called, nor is it guaranteed to collect objects in a specific order. In fact, if you omit the call to WaitForPendingFinalizers, it is likely that lines 3940 will execute before the garbage collector has a chance to call the destructors.
[Note: A method declared static cannot access non-static class members directly, because a static method can be called even when no objects of the class exist. For the same reason, the this reference cannot be used in a static methodthe this reference must refer to a specific object of the class, and when a static method is called, there might not be any objects of its class in memory. The this reference is required to allow a method of a class to access non-static members of the same class.]
|
readonly Instance Variables
|