10.9 static Class Members

Every object has its own copy of its class’s instance variables. In certain cases, only one copy of a particular variable should be shared by all objects of a class. A static variable (or property) is used in such cases. A static variable or property represents classwide information—all objects of the class share the same piece of data. The declaration of a static variable or property 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’s 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 (or as a property, but we’ll use an instance variable for discussion purposes here). If we do this, every Martian will have a separate copy of the instance variable, and every time we create a new Martian, we’ll 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, but only one copy of the static martianCount is maintained. This saves space. We save time by having the Martian constructor increment the static martianCount—there’s only one copy, so we do not have to increment separate copies of martianCount for each Martian object.

Software Engineering Observation 10.10

Use a static variable when all objects of a class must share the same copy of the variable.

static Variable Scope

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 member access (.) operator, as in Math.PI. A class’s private static class members can be accessed only through the class’s methods and properties. To access a private static member from outside its class, a public static method or property can be provided.

Common Programming Error 10.2

It’s a compilation error to access or invoke a static member by referencing it through an instance of the class, like a non-static member.

 

Software Engineering Observation 10.11

static variables, methods and properties exist, and can be used, even if no objects of that class have been instantiated. static members are available as soon as the class is loaded into memory at execution time.

static Methods and Non-static Class Members

A static method (or property) cannot access non-static class members directly, because a static method (or property) can be called even when no objects of the class exist. For the same reason, this cannot be used in a static method—the this reference always refers to a specific object of the class. When a static method is called, it does not know which object to manipulate and there might not be any objects of its class in memory.

Class Employee

Our next app contains classes Employee (Fig. 10.10) and EmployeeTest (Fig. 10.11). Class Employee declares private static auto-implemented property Count to maintain a count of the number of Employee objects that have been created. We declare Count’s set accessor private, because only class Employee should be able to modify Count’s value. Count is a static auto-implemented property, so the compiler creates a corresponding private static variable that Count manages. When you declare a static variable and do not initialize it, the compiler initializes it to the type’s default value (in this case, 0).

When Employee objects exist, Count can be used in any method or property of class Employee—this example increments Count in the constructor (line 19). Client code can access the Count with the expression Employee.Count, which evaluates to the number of Employee objects that have been created.

Fig. 10.10 static property used to maintain a count of the number of Employee objects that have been created.

Alternate View

 1    // Fig. 10.10: Employee.cs
 2    // static property used to maintain a count of the number of
 3    // Employee objects that have been created.
 4    using System;
 5
 6    public class Employee
 7    {
 8       public static int Count { get; private set; } // objects in memory
 9
10       public string FirstName { get; }
11       public string LastName { get; }
12
13       // initialize employee, add 1 to static Count and
14       // output string indicating that constructor was called
15       public Employee(string firstName, string lastName)
16       {
17          FirstName = firstName;
18          LastName = lastName;
19          ++Count; // increment static count of employees
20          Console.WriteLine("Employee constructor: " +
21             $"{FirstName} {LastName}; Count = {Count}");
22        }
23    }

Class EmployeeTest

EmployeeTest method Main (Fig. 10.11) instantiates two Employee objects (lines 14–15). When each object’s constructor is invoked, lines 17–18 of Fig. 10.10 assign the Employee’s first name and last name to properties FirstName and LastName. These two statements do not make copies of the original string arguments.

Software Engineering Observation 10.12

Actually, string objects in C# are immutable—they cannot be modified after they’re created. Therefore, it’s safe to have many references to one string. This is not normally the case for objects of most other classes. If string objects are immutable, you might wonder why we’re able to use operators + and += to concatenate strings. String-concatenation operations actually result in a new string object containing the concatenated values. The original string objects are not modified.

Fig. 10.11 static member demonstration.

Alternate View

 1    // Fig. 10.11: EmployeeTest.cs
 2    // static member demonstration.
 3    using System;
 4
 5    class EmployeeTest
 6    {
 7       static void Main()
 8       {
 9          // show that Count is 0 before creating Employees
10          Console.WriteLine(
11             $"Employees before instantiation: {Employee.Count}");
12
13          // create two Employees; Count should become 2
14          var e1 = new Employee("Susan", "Baker");
15          var e2 = new Employee("Bob", "Blue");   
16
17         // show that Count is 2 after creating two Employees
18         Console.WriteLine(
19           $"
Employees after instantiation: {Employee.Count}");
20
21         // get names of Employees
22         Console.WriteLine($"
Employee 1: {e1.FirstName}{e1.LastName}");
23         Console.WriteLine($"Employee 2: {e2.FirstName}{e2.LastName}");
24
25        // in this example, there is only one reference to each Employee,
26        // so the following statements cause the CLR to mark each        
27        // Employee object as being eligible for garbage collection      
28        e1 = null; // mark object referenced by e1 as no longer needed   
29        e2 = null; // mark object referenced by e2 as no longer needed   
30       }
31    }

Employees before instantiation: 0
Employee constructor: Susan Baker; Count = 1
Employee constructor: Bob Blue; Count = 2

Employees after instantiation: 2

Employee 1: Susan Baker
Employee 2: Bob Blue

Lines 18–19 of Fig. 10.11 display the updated Count. When Main has finished using the two Employee objects, references e1 and e2 are set to null at lines 28–29, so they no longer refer to the objects that were instantiated in lines 14–15. The objects become eligible for destruction because there are no more references to them. After the objects’ destructors are called, the objects become eligible for garbage collection. (Note that we did not need to set e1 and e2 are set to null here as they’re local variables—when a local variable of a reference type goes out of scope, the object’s reference count is decremented automatically.)

Eventually, the garbage collector might reclaim the memory for these objects (or the operating system will reclaim it when the app terminates). C# does not guarantee when, or even whether, the garbage collector will execute. When the garbage collector does run, it’s possible that no objects or only a subset of the eligible objects will be collected.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset