Konstruktor statyczny służy do inicjowania dowolnych danych statycznych lub wykonywania konkretnej akcji, która musi być wykonywana tylko raz. Jest wywoływane automatycznie przed utworzeniem pierwszego wystąpienia lub odwołaniem do jakichkolwiek statycznych członków. Konstruktor statyczny jest wywoływany co najwyżej raz.
class SimpleClass
{
static readonly long baseline;
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}
Istnieje kilka akcji, które są częścią inicjowania statycznego. Te działania są podejmowane w następującej kolejności:
-
Pola statyczne są ustawione na 0. Zazwyczaj to inicjowanie jest wykonywane przez środowisko uruchomieniowe.
-
Uruchamiane są inicjatory pól statycznych. Inicjalizatory pól statycznych uruchamiają się w najbardziej pochodnym typie.
-
Uruchamiane są inicjatory pól statycznych typu podstawowego. Inicjalizatory pól statycznych rozpoczynające się od bezpośredniej bazy poprzez każdy typ bazowy do System.Object.
-
Każdy konstruktor statyczny uruchamia się. Wszystkie konstruktory statyczne , od ostatecznej klasy bazowej Object.Object przez każdą klasę bazową przez przebieg typu. Kolejność wykonywania konstruktora statycznego nie jest określona. Jednak wszystkie konstruktory statyczne w hierarchii są uruchamiane przed utworzeniem jakichkolwiek wystąpień.
Ważne
Istnieje jeden ważny wyjątek od reguły, że konstruktor statyczny zostaje uruchomiony przed utworzeniem dowolnego wystąpienia. Jeśli inicjator pola statycznego tworzy wystąpienie typu, jest uruchamiany (w tym dowolne wywołanie konstruktora wystąpienia) zanim uruchomi się konstruktor statyczny. Jest to najczęściej spotykane we wzorcu singleton, jak pokazano w poniższym przykładzie:
public class Singleton
{
private static Singleton instance = new Singleton();
private Singleton()
{
Console.WriteLine("Executes before static constructor.");
}
static Singleton()
{
Console.WriteLine("Executes after instance constructor.");
}
public static Singleton Instance => instance;
}
Inicjator modułu może być alternatywą dla konstruktora statycznego. Aby uzyskać więcej informacji, zobacz specyfikację inicjatorów modułów.
Konstruktory statyczne mają następujące właściwości:
- Konstruktor statyczny nie przyjmuje modyfikatorów dostępu ani nie ma parametrów.
- Klasa lub struktura może mieć tylko jeden konstruktor statyczny.
- Konstruktory statyczne nie mogą być dziedziczone ani przeciążone.
- Konstruktor statyczny nie może być wywoływany bezpośrednio i ma być wywoływany tylko przez środowisko uruchomieniowe języka wspólnego (CLR). Jest wywoływany automatycznie.
- Użytkownik nie ma kontroli nad tym, kiedy konstruktor statyczny jest wykonywany w programie.
- Konstruktor statyczny jest wywoływany automatycznie. Inicjuje klasę przed utworzeniem pierwszego wystąpienia lub zadeklarowaniem jakichkolwiek statycznych składowych w tej klasie (a nie w jej klasach bazowych). Konstruktor statyczny jest uruchamiany przed konstruktorem instancji. Jeśli inicjatory zmiennych statycznych pól znajdują się w klasie konstruktora statycznego, są uruchamiane w kolejności tekstowej, w której są wyświetlane w deklaracji klasy. Inicjatory są uruchamiane bezpośrednio przed konstruktorem statycznym.
- Jeśli nie podasz konstruktora statycznego do inicjowania pól statycznych, wszystkie pola statyczne są inicjowane do ich wartości domyślnej, jak pokazano w sekcji Domyślne wartości typów języka C#.
- Jeśli konstruktor statyczny zgłasza wyjątek, środowisko uruchomieniowe nie wywołuje go po raz drugi, a typ pozostaje niezainicjowany przez okres istnienia domeny aplikacji. Najczęściej wyjątek TypeInitializationException zgłaszany jest, gdy konstruktor statyczny nie może utworzyć wystąpienia typu lub dla nieobsługiwanego wyjątku występującego w konstruktorze statycznym. W przypadku konstruktorów statycznych, które nie są jawnie zdefiniowane w kodzie źródłowym, rozwiązywanie problemów może wymagać inspekcji kodu języka pośredniego (IL).
- Obecność konstruktora statycznego uniemożliwia dodanie atrybutu BeforeFieldInit typu. Ogranicza to optymalizację środowiska uruchomieniowego.
- Pole zadeklarowane jako
static readonly
można przypisać tylko jako część deklaracji lub w konstruktorze statycznym. Gdy jawny konstruktor statyczny nie jest wymagany, zainicjuj pola statyczne w deklaracji, a nie za pomocą konstruktora statycznego w celu uzyskania lepszej optymalizacji środowiska uruchomieniowego.
- Środowisko uruchomieniowe wywołuje konstruktor statyczny nie więcej niż raz w jednej domenie aplikacji. To wywołanie jest wykonywane w zablokowanym regionie na podstawie konkretnego typu klasy. W konstruktorze statycznym nie są potrzebne żadne dodatkowe mechanizmy blokowania. Aby uniknąć ryzyka zakleszczenia, nie blokuj bieżącego wątku w konstruktorach statycznych i inicjalizatorach. Na przykład nie czekaj na zadania, wątki, uchwyty oczekiwania lub zdarzenia, nie pobieraj blokad i nie wykonuj blokujących operacji równoległych, takich jak pętle równoległe
Parallel.Invoke
i równoległe zapytania LINQ.
Uwaga
Chociaż nie jest dostępny bezpośrednio, obecność jawnego konstruktora statycznego powinna być udokumentowana w celu ułatwienia rozwiązywania problemów z wyjątkami inicjowania.
- Typowym zastosowaniem konstruktorów statycznych jest użycie, gdy klasa używa pliku dziennika, a konstruktor jest używany do zapisywania wpisów w tym pliku.
- Konstruktory statyczne są również przydatne podczas tworzenia klas osłonowych dla niezarządzanego kodu, gdy konstruktor może wywołać metodę
LoadLibrary
.
- Konstruktory statyczne są również wygodnym miejscem do wymuszania kontroli czasu wykonywania dla parametru typu, którego nie można sprawdzić w czasie kompilacji za pomocą ograniczeń parametrów typu.
W tym przykładzie klasa Bus
ma konstruktor statyczny. Gdy powstaje pierwsze wystąpienie Bus
(bus1
), zostaje wywołany konstruktor statyczny w celu zainicjowania klasy. Przykładowe dane wyjściowe weryfikują, że konstruktor statyczny uruchamiany jest tylko raz, mimo że są tworzone dwa wystąpienia Bus
i że uruchamiany jest zanim uruchomi się konstruktor instancji.
public class Bus
{
protected static readonly DateTime globalStartTime;
protected int RouteNumber { get; set; }
static Bus()
{
globalStartTime = DateTime.Now;
Console.WriteLine($"Static constructor sets global start time to {globalStartTime.ToLongTimeString()}");
}
public Bus(int routeNum)
{
RouteNumber = routeNum;
Console.WriteLine($"Bus #{RouteNumber} is created.");
}
public void Drive()
{
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
Console.WriteLine($"{this.RouteNumber} is starting its route {elapsedTime.Milliseconds:N2} minutes after global start time {globalStartTime.ToShortTimeString()}.");
}
}
class TestBus
{
static void Main()
{
Bus bus1 = new Bus(71);
Bus bus2 = new Bus(72);
bus1.Drive();
System.Threading.Thread.Sleep(25);
bus2.Drive();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
Aby uzyskać więcej informacji, zobacz sekcję Konstruktory statyczne specyfikacji języka C#.