Konstruktory statyczne (Przewodnik programowania w języku C#)

Konstruktor statyczny służy do inicjowania dowolnych danych statycznych lub wykonywania konkretnej akcji, która musi być wykonywana tylko raz. Jest ona wywoływana automatycznie przed utworzeniem pierwszego wystąpienia lub odwołaniami do wszystkich statycznych elementów członkowskich. Konstruktor statyczny będzie wywoływany co najwyżej raz.

class SimpleClass
{
    // Static variable that must be initialized at run time.
    static readonly long baseline;

    // Static constructor is called at most one time, before any
    // instance constructor is invoked or member is accessed.
    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:

  1. Pola statyczne są ustawione na 0. Jest to zwykle wykonywane przez środowisko uruchomieniowe.
  2. Uruchamiane są inicjatory pól statycznych. Inicjatory pól statycznych w najbardziej pochodnym przebiegu typu.
  3. Uruchamiane są inicjatory pól statycznych typu podstawowego. Inicjatory pól statycznych rozpoczynające się od bezpośredniej podstawy przez każdy typ podstawowy do System.Object.
  4. Uruchamiane są podstawowe konstruktory statyczne. Wszystkie konstruktory statyczne, począwszy od Object.Object każdej klasy bazowej do bezpośredniej klasy bazowej.
  5. Konstruktor statyczny jest uruchamiany. Konstruktor statyczny dla typu jest uruchamiany.

Inicjator modułu może być alternatywą dla konstruktora statycznego. Aby uzyskać więcej informacji, zobacz specyfikację inicjatorów modułów.

Uwagi

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 odwołaniem do wszystkich statycznych składowych zadeklarowanych w tej klasie (a nie jej klas bazowych). Konstruktor statyczny jest uruchamiany przed konstruktorem wystąpienia. Jeśli inicjatory zmiennych statycznych pól znajdują się w klasie konstruktora statycznego, są one wykonywane w kolejności tekstowej, w której są wyświetlane w deklaracji klasy. Inicjatory są uruchamiane bezpośrednio przed wykonaniem konstruktora statycznego.
  • 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 pozostanie niezainicjowany przez okres istnienia domeny aplikacji. Najczęściej zgłaszany jest wyjątek, TypeInitializationException 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że być przypisane 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 określonego typu klasy. W treści konstruktora statycznego nie są potrzebne żadne dodatkowe mechanizmy blokowania. Aby uniknąć ryzyka zakleszczenia, nie blokuj bieżącego wątku w konstruktorach statycznych i inicjatorach. 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.

Użycie

  • 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 otoki 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 pośrednictwem ograniczeń parametrów typu.

Przykład

W tym przykładzie klasa Bus ma konstruktor statyczny. Po utworzeniu Bus pierwszego wystąpienia (bus1) konstruktor statyczny jest wywoływany w celu zainicjowania klasy. Przykładowe dane wyjściowe weryfikują, czy konstruktor statyczny jest uruchamiany tylko raz, mimo że są tworzone dwa wystąpienia Bus , i że jest uruchamiany przed uruchomieniem konstruktora wystąpienia.

public class Bus
{
    // Static variable used by all Bus instances.
    // Represents the time the first bus of the day starts its route.
    protected static readonly DateTime globalStartTime;

    // Property for the number of each bus.
    protected int RouteNumber { get; set; }

    // Static constructor to initialize the static variable.
    // It is invoked before the first instance constructor is run.
    static Bus()
    {
        globalStartTime = DateTime.Now;

        // The following statement produces the first line of output,
        // and the line occurs only once.
        Console.WriteLine("Static constructor sets global start time to {0}",
            globalStartTime.ToLongTimeString());
    }

    // Instance constructor.
    public Bus(int routeNum)
    {
        RouteNumber = routeNum;
        Console.WriteLine("Bus #{0} is created.", RouteNumber);
    }

    // Instance method.
    public void Drive()
    {
        TimeSpan elapsedTime = DateTime.Now - globalStartTime;

        // For demonstration purposes we treat milliseconds as minutes to simulate
        // actual bus times. Do not do this in your actual bus schedule program!
        Console.WriteLine("{0} is starting its route {1:N2} minutes after global start time {2}.",
                                this.RouteNumber,
                                elapsedTime.Milliseconds,
                                globalStartTime.ToShortTimeString());
    }
}

class TestBus
{
    static void Main()
    {
        // The creation of this instance activates the static constructor.
        Bus bus1 = new Bus(71);

        // Create a second bus.
        Bus bus2 = new Bus(72);

        // Send bus1 on its way.
        bus1.Drive();

        // Wait for bus2 to warm up.
        System.Threading.Thread.Sleep(25);

        // Send bus2 on its way.
        bus2.Drive();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Sample output:
    Static constructor sets global start time to 3:57:08 PM.
    Bus #71 is created.
    Bus #72 is created.
    71 is starting its route 6.00 minutes after global start time 3:57 PM.
    72 is starting its route 31.00 minutes after global start time 3:57 PM.
*/

specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz sekcję Konstruktory statyczne specyfikacji języka C#.

Zobacz też