Static Fields
Fields can be declared as static in the same
manner as methods and properties. The addition of the static keyword
indicates that a field is accessed using its class name, rather than via an
instance of the class. When you create a class that contains static fields, it
is important that you initialize them in the correct order. Consider the
following code:
class AttemptController
{
static int Threshold
= MaxAttempts - WarningAttempts;
static int
MaxAttempts = 5;
static int WarningAttempts
= 2;
}
At a glance the above code looks OK. The MaxAttempts
value is set to 5, the WarningAttempts field to 2 and the Threshold
value to the difference of the other two fields. You might expect the
Threshold value to be 3. However, if you run the following code to output
the values, you will see results that you may not expect:
Console.WriteLine("Maximum: {0}",
AttemptController.MaxAttempts);
Console.WriteLine("Warning: {0}",
AttemptController.WarningAttempts);
Console.WriteLine("Threshold:
{0}", AttemptController.Threshold);
/* OUTPUT
Maximum: 5
Warning: 2
Threshold: 0
*/
As you can see in the comment, it appears that the
Threshold field's value is being incorrectly calculated.
C# Specification
The Threshold field cannot be evaluated
until MaxAttempts and WarningAttempts being correctly initialise.
If the initialisation is fail to evaluate, then the default value of its type
will return. For integer values, this is zero.
The C# Language Specification also tells us that
when static fields are initialised by applying a value in their declaration, as
we have done above, they are set in the order in which they appear in the code.
This means that when the Threshold value is calculated, we are using two
uninitialised values, each of which yields a result of zero. The Threshold
field's calculation is therefore the result of zero minus zero, which is zero.
A simple fix is to reorder the declarations to
ensure that the MaxAttempts and WarningAttempts values are
present before they are needed, as shown below:
class AttemptController
{
static int
MaxAttempts = 5;
static int
WarningAttempts = 2;
static int Threshold
= MaxAttempts - WarningAttempts;
}
Running the program now gives the correct results:
Maximum: 5
Warning: 2
Threshold: 3
But this approach does not generate the most
readable and maintainable code. It is quite possible that another developer may
change the order of the fields at a later time, may be to sort them
alphabetically, unknowingly introducing a bug. A better fix is to remove the
initialisation from the field declarations and instead set the values within a
static constructor, as shown below:
class AttemptController
{
static int
MaxAttempts;
static int
WarningAttempts;
static int Threshold;
static AttemptController()
{
MaxAttempts = 5;
WarningAttempts = 2;
Threshold = MaxAttempts - WarningAttempts;
}
}