Jak działa konstruktor statyczny?

82
namespace MyNameSpace
{
    static class MyClass
    {
        static MyClass()
        {
            //Authentication process.. User needs to enter password
        }

        public static void MyMethod()
        {
            //Depends on successful completion of constructor
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass.MyMethod();
        }
    }
}

Oto kolejność, którą założyłem

  1. Początek konstruktora statycznego
  2. Koniec konstruktora statycznego
  3. Początek pliku main
  4. Początek MyMethod
  5. Koniec main

Teraz, w każdym scenariuszu, jeśli 4 zacznie się przed 2, mam przerąbane. Czy to możliwe?

om471987
źródło
8
Czy to jest pytanie dotyczące języka Java czy C #? Umieściłeś oba tagi i nie sądzę, aby specyfikacja była taka sama w obu językach.
ARRG
W moim otwarciu to działa tak samo dla obu ... Ale jestem facetem z C # .. Proszę o to
om471987,
4
Java nie ma w ten sam sposób statycznego konstruktora, tylko statyczne bloki do statycznej inicjalizacji. static {// zrób coś ...}
deraj,
2
Osobiście czuję się nieswojo z jakąkolwiek formą interaktywności wewnątrz statycznego konstruktora. Rozumiem twój cel (spraw, aby każda metoda w tej statycznej klasie czekała, aż użytkownik zostanie autoryzowany przed zezwoleniem na jej uruchomienie), ale naprawdę nie podoba mi się ta metoda wykonywania tego.
Brian
@Brian: - Tak ... masz rację ... Ja po prostu robi analiza .. W końcu po prostu nie zdecydował się użyć konstruktora, ale metoda Initialize
om471987

Odpowiedzi:

220

Zadałeś tutaj tylko jedno pytanie, ale jest kilkanaście pytań, które powinieneś był zadać, więc odpowiem na wszystkie.

Oto kolejność, którą założyłem

  1. Początek konstruktora klasy (znany również jako cctor)
  2. Koniec cctor
  3. początek Main
  4. początek MyMethod

Czy to jest poprawne?

Nie. Prawidłowa kolejność to:

  1. Początek cctora programu, jeśli taki istnieje. Tam nie ma.
  2. Koniec cctor dla programu, jeśli taki istnieje. Tam nie ma.
  3. Początek Main
  4. Uruchomienie cctora dla MyClass
  5. Koniec procesora MyClass
  6. Początek MyClass.MyMethod

A co, jeśli istnieje inicjator pola statycznego?

Środowisko CLR może zmienić kolejność, w jakiej w niektórych przypadkach są uruchamiane inicjatory pól statycznych. Zobacz stronę Jona na ten temat, aby uzyskać szczegółowe informacje:

Różnice między konstruktorami statycznymi a inicjatorami typów

Czy jest kiedykolwiek możliwe, aby metoda statyczna, taka jak, MyMethodbyła wywoływana przed zakończeniem działania cctora tej klasy?

Tak. Jeśli sam cctor wywoła MyMethod, wtedy oczywiście MyMethod zostanie wywołana przed zakończeniem cctor.

Moduł cctor nie wywołuje MyMethod. Czy jest możliwe kiedykolwiek MyMethodwywołanie metody statycznej, takiej jak metoda cctor MyClass?

Tak. Jeśli cctor używa innego typu, którego cctor wywołuje MyMethod, wówczas MyMethod zostanie wywołana przed zakończeniem działania cctor MyClass.

Żaden procesor nie wywołuje MyMethod, bezpośrednio ani pośrednio! Czy jest kiedykolwiek możliwe, aby metoda statyczna, taka jak, MyMethodbyła wywoływana przed zakończeniem działania modułu sterującego MyClass?

Nie.

Czy to nadal prawda, nawet jeśli w grę wchodzi wiele wątków?

Tak. Cctor zakończy się na jednym wątku, zanim będzie można wywołać metodę statyczną w dowolnym wątku.

Czy cctor można wywołać więcej niż raz? Załóżmy, że oba wątki powodują uruchomienie cctora.

Gwarantowane jest, że cctor zostanie wywołany najwyżej raz, niezależnie od liczby zaangażowanych wątków. Jeśli dwa wątki wywołują MyMethod „w tym samym czasie”, ścigają się. Jeden z nich przegrywa wyścig i blokuje do czasu, gdy na zwycięskiej nitce nie skończy się kreator MyClass.

Utrata wątku blokuje się, dopóki cctor nie zostanie zakończony? Naprawdę ?

Naprawdę.

A co, jeśli cctor w wątku wygrywającym wywoła kod, który blokuje blokadę poprzednio zajętą przez wątek przegrywający ?

Wtedy mamy klasyczny warunek odwrócenia kolejności blokowania. Twój program jest zakleszczony. Na zawsze.

To wydaje się niebezpieczne. Jak mogę uniknąć impasu?

Jeśli to boli, przestań to robić . Nigdy nie rób czegoś, co może zablokować cctor.

Czy warto polegać na semantyce inicjalizacji cctor w celu wymuszenia złożonych wymagań dotyczących bezpieczeństwa? Czy warto mieć moduł sterujący, który obsługuje interakcje użytkowników?

Nie są też dobre pomysły. Moja rada jest taka, że ​​powinieneś znaleźć inny sposób, aby upewnić się, że warunki wstępne mające wpływ na bezpieczeństwo są spełnione.

Eric Lippert
źródło
5
Eric, jestem ciekaw, dlaczego w tej odpowiedzi zastąpiłeś „statyczny konstruktor” na „konstruktor klasy” lub „cctor”. Czy używanie „konstruktora statycznego” w odniesieniu do elementu cctor jest niewłaściwe?
phoog
6
@phoog: Chciałem być konsekwentny w używaniu terminologii, więc wybrałem najkrótszą. „Konstruktor statyczny” i „Konstruktor klasy” są w porządku. Jako szczegół implementacji, konstruktor statyczny typu jest emitowany jako specjalna metoda o nazwie „.cctor”, dlatego często określa się taki konstruktor jako „cctor”. Gdybym pisał w bardziej formalnym kontekście, użyłbym jednego z dłuższych terminów.
Eric Lippert
@EricLippert Czy to samo dotyczy klas niestatycznych z konstruktorem statycznym?
Legends
2
@Legends: Czy to samo dotyczy klas niestatycznych z konstruktorem statycznym? Tak.
Eric Lippert
24

Według MSDN , konstruktor statyczny:

Konstruktor statyczny jest wywoływany automatycznie w celu zainicjowania klasy przed utworzeniem pierwszego wystąpienia lub przywołaniem jakichkolwiek statycznych elementów członkowskich.

Zatem konstruktor statyczny zostanie wywołany przed MyClass.MyMethod()wywołaniem metody statycznej (zakładając oczywiście, że nie zostanie on również wywołany podczas konstrukcji statycznej lub inicjalizacji pola statycznego).

Jeśli robisz w tym coś asynchronicznego static constructor, to Twoim zadaniem jest to zsynchronizować.

James Michael Hare
źródło
7
Jeśli robisz coś asynchronicznego, co obejmuje drugi wątek w statycznym konstruktorze, czeka cię świat bólu . Nic nie przyspiesza impasu. Przykład można znaleźć na stackoverflow.com/a/8883117/88656 .
Eric Lippert
@Eric: zgodził się ... Nie chciałbym, aby to zrobić, ale nie był pewien, tworząc jego przykładem, co dokładnie on chce być wykończone przez czas myMethod nazwano ...
James Michael Hare
11

# 3 to właściwie # 1: inicjalizacja statyczna nie rozpoczyna się aż do pierwszego użycia klasy, do której należy.

Jest to możliwe, jeśli MyMethodjest wywoływana z konstruktora statycznego lub statycznego bloku inicjalizacji. Jeśli nie wywołujesz MyMethodbezpośrednio lub pośrednio z konstruktora statycznego, wszystko powinno być w porządku.

Sergey Kalinichenko
źródło
Uwaga: rozumiem, że staticinicjalizację można w rzeczywistości wywołać przed pierwszym użyciem, w zależności od uprawnień do optymalizacji.
James Michael Hare
1
W przypadku statycznego konstruktora to prawda, ale moim celem była statyczna inicjalizacja. Przepraszam, może właśnie wybierałem nity na frazie „statyczna inicjalizacja nie rozpoczyna się ...” to prawda w przypadku konstrukcji statycznej, ale nie jeśli klasa nie ma statycznego konstruktora, wówczas statyczna inicjalizacja może nastąpić wcześniej.
James Michael Hare
Przepraszam, prawdopodobnie po prostu przesadnie analizuję słownictwo. W kontekście pytania jest całkowicie poprawne, martwiłem się tylko tym zdaniem jako samodzielną instrukcją do statycznej inicjalizacji w kontekście klas bez jawnych statycznych konstruktorów.
James Michael Hare
@James: Nie przesadzasz - terminologia jest tutaj kluczową różnicą. Konstruktory statyczne to koncepcja języka C #, podczas gdy inicjalizacja typów to kwestia .NET. Kod wewnątrz konstruktora statycznego (C #) staje się częścią inicjatora typu (.NET), ale kiedy i w jaki sposób wyzwalacze inicjatora typu (tj. beforefieldinitSemantyka) są określane przez to, czy klasa C # ma konstruktor statyczny.
LukeH
9

Z dokumentacji (wyróżnienie moje):

Statyczny konstruktor nazywa automatycznie zainicjować klasę przed pierwszą instancją jest tworzony lub jakiekolwiek statyczne członkowie są oznaczone .

rozpoznać
źródło
2

Możesz zagwarantować, że liczba 4 będzie zawsze występować po 2 (jeśli nie utworzysz instancji swojej klasy z metody statycznej), jednak to samo nie dotyczy 1 i 3.

Scott Chamberlain
źródło
2

Konstruktor statyczny zostanie wywołany przed wykonaniem mymethod. Jeśli jednak masz spieprzone, jeśli 4 zostanie sprawdzone przed 2, proponuję ponownie przemyśleć swój projekt. I tak nie powinienem wykonywać skomplikowanych czynności w konstruktorze statycznym.

Umair
źródło
2

Środowisko CLR gwarantuje, że Konstruktor statyczny jest uruchamiany przed uzyskaniem dostępu do jakichkolwiek statycznych elementów członkowskich. Jednak twój projekt jest nieco śmierdzący. Byłoby łatwiej zrobić coś takiego:

static void Main(string[] args) 
{ 
     bool userIsAuthenticated = MyClass.AuthenticateUser();
     if (userIsAuthenticated)
         MyClass.MyMethod(); 
 } 

W przypadku projektu, jeśli uwierzytelnianie nie powiedzie się, jedynym sposobem, aby zapobiec uruchomieniu MyMethod, jest zgłoszenie wyjątku.

phoog
źródło
2

Zapewniono, że konstruktor klasy statycznej został wywołany przed wykonaniem którejkolwiek z jej metod. Przykład:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Press enter");
        Console.ReadLine();
        Boop.SayHi();
        Boop.SayHi();
        Console.ReadLine();
    }

}

static class Boop
{
    static Boop()
    {
        Console.WriteLine("Hi incoming ...");
    }

    public static void SayHi()
    {
        Console.WriteLine("Hi there!");
    }
}

Wynik:

Naciśnij enter

// po wciśnięciu enter

Cześć nadchodzący ...

Cześć!

Cześć!

haiyyu
źródło
using System; przestrzeń nazw MyNameSpace {class Program {static void Main (string [] args) {Console.WriteLine ("Entered in main"); Boop.SayHi (); Boop.SayHi (); }} klasa statyczna Boop {static Boop () {Console.Read (); Console.WriteLine ("Wprowadzono klucz konstruktora"); } public static void SayHi () {Console.WriteLine ("Metoda została wywołana"); }}} tak, ten program daje lepsze zrozumienie
om471987,
Możliwie. Jednak następnym razem opublikuj to jako odpowiedź. Wtedy jest to bardziej pomocne i widoczne.
haiyyu
1

Oto rzeczywista kolejność, w jakiej wszystko się dzieje:

  1. Początek Main
  2. Początek MyClasskonstruktora statycznego
  3. Koniec MyClasskonstruktora statycznego
  4. Początek MyMethod
  5. Koniec Main

źródło
0

Lub możesz przejść przez debuger.

Saille
źródło