Professional C# 6 and .NET Core 1.0. Christian Nagel
Читать онлайн книгу.Singleton class contains a private constructor, so you can instantiate it only within the class itself. To instantiate it, the static property Instance returns the field s_instance. If this field is not yet initialized (null), a new instance is created by calling the instance constructor. For the null check, the coalescing operator is used. If the left side of this operator is null, the right side of this operator is processed and the instance constructor invoked.
NOTE The coalescing operator is explained in detail in Chapter 8.
Calling Constructors from Other Constructors
You might sometimes find yourself in the situation where you have several constructors in a class, perhaps to accommodate some optional parameters for which the constructors have some code in common. For example, consider the following:
Both constructors initialize the same fields. It would clearly be neater to place all the code in one location. C# has a special syntax known as a constructor initializer to enable this:
In this context, the this keyword simply causes the constructor with the nearest matching parameters to be called. Note that any constructor initializer is executed before the body of the constructor. Suppose that the following code is run:
In this example, the two-parameter constructor executes before any code in the body of the one-parameter constructor (though in this particular case, because there is no code in the body of the one-parameter constructor, it makes no difference).
A C# constructor initializer may contain either one call to another constructor in the same class (using the syntax just presented) or one call to a constructor in the immediate base class (using the same syntax, but using the keyword base instead of this). It is not possible to put more than one call in the initializer.
Static Constructors
One feature of C# is that it is also possible to write a static no-parameter constructor for a class. Such a constructor is executed only once, unlike the constructors written so far, which are instance constructors that are executed whenever an object of that class is created:
One reason for writing a static constructor is if your class has some static fields or properties that need to be initialized from an external source before the class is first used.
The .NET runtime makes no guarantees about when a static constructor will be executed, so you should not place any code in it that relies on it being executed at a particular time (for example, when an assembly is loaded). Nor is it possible to predict in what order static constructors of different classes will execute. However, what is guaranteed is that the static constructor will run at most once, and that it will be invoked before your code makes any reference to the class. In C#, the static constructor is usually executed immediately before the first call to any member of the class.
Note that the static constructor does not have any access modifiers. It’s never called explicitly by any other C# code, but always by the .NET runtime when the class is loaded, so any access modifier such as public or private would be meaningless. For this same reason, the static constructor can never take any parameters, and there can be only one static constructor for a class. It should also be obvious that a static constructor can access only static members, not instance members, of the class.
It is possible to have a static constructor and a zero-parameter instance constructor defined in the same class. Although the parameter lists are identical, there is no conflict because the static constructor is executed when the class is loaded, but the instance constructor is executed whenever an instance is created. Therefore, there is no confusion about which constructor is executed or when.
If you have more than one class that has a static constructor, the static constructor that is executed first is undefined. Therefore, you should not put any code in a static constructor that depends on other static constructors having been or not having been executed. However, if any static fields have been given default values, these are allocated before the static constructor is called.
The next example illustrates the use of a static constructor. It is based on the idea of a program that has user preferences (which are presumably stored in some configuration file). To keep things simple, assume just one user preference – a quantity called BackColor that might represent the background color to be used in an application. Because we don’t want to get into the details of writing code to read data from an external source here, assume also that the preference is to have a background color of red on weekdays and green on weekends. All the program does is display the preference in a console window, but that is enough to see a static constructor at work.
The class UserPreferences is declared with the static modifier; thus it cannot be instantiated and can only contain static members. The static constructor initializes the BackColor property depending on the day of the week (code file StaticConstructorSample/UserPreferences.cs):
This code makes use of the System.DateTime struct that is supplied with the .NET Framework. DateTime implements a static property Now that returns the current time. DayOfWeek is an instance property of DateTime that returns an enum value of type DayOfWeek.
Color is defined as an enum type and contains a few colors. The enum types are explained in detail later in the section Enums (code file StaticConstructorSample/Enum.cs):
The Main method just invokes the WriteLine method and writes the user preferences back color to the console (code file StaticConstructorSample/Program.cs):
Compiling and running the preceding code results in the following output:
Of course, if the code is executed during the weekend, your color preference would be Green.
Readonly Members
If you do not want to change a data member after initialization, the readonly keyword can be used. Let’s get into the details of readonly fields and readonly properties.
Readonly Fields
To guarantee that fields of an object cannot be changed, fields can be declared with the readonly modifier. Fields with the readonly modifier can be assigned only values from constructors. This is different from the const modifier. With the const modifier, the compiler replaces the variable by its value everywhere it is used. The compiler already knows the value of the constant. Read-only fields are assigned during runtime from a constructor. Contrary to const fields, read-only fields can be instance members. For using a read-only field as a class member, the static modifier needs to be assigned to the field.
Suppose that you have a program that edits documents, and for licensing reasons you want to restrict the number of documents that can be opened simultaneously. Assume also that you are selling different versions of the software, and it’s possible for customers to upgrade their licenses to open more documents simultaneously. Clearly, this means you can’t hard-code the maximum number in the source code. You would probably need a field to represent this maximum number. This field has to be read in – perhaps from a registry key or some other file storage – each time the program is launched. Therefore, your code might look something like this:
In this case, the field is static because the maximum number of documents needs to be stored only once per running instance of the program. This is why it is initialized in the static constructor. If you had an instance readonly field, you would initialize it in the instance constructor(s). For example, presumably each document you edit has a creation date, which you wouldn’t want to allow the user to change (because that would be rewriting the past!).
As