static
Class MembersEvery 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 Martian
s 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 Martian
s present. If fewer than five Martian
s 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.
Use a static
variable when all objects of a class must share the same copy of the variable.
static
Variable ScopeThe 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.
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.
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 MembersA 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.
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.
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.
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 string
s. String-concatenation operations actually result in a new string
object containing the concatenated values. The original string objects are not modified.
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.