Wie funktioniert ein statischer Konstruktor?

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();
        }
    }
}

Hier ist die Reihenfolge, die ich angenommen habe

  1. Start des statischen Konstruktors
  2. Ende des statischen Konstruktors
  3. Start der Hauptleitung
  4. Start von MyMethod
  5. Ende der Hauptleitung

Jetzt in jedem Szenario, wenn 4 vor 2 startet, bin ich geschraubt. Ist es möglich?

om471987
quelle
8
Ist das eine Java- oder C # -Frage? Sie haben beide Tags eingefügt, und ich glaube nicht, dass die Spezifikation in beiden Sprachen gleich ist.
ARRG
In meiner Eröffnung ist dies für beide gleich. Aber ich bin ein C #
-Typ
4
Java hat keinen statischen Konstruktor auf die gleiche Weise, sondern nur statische Blöcke für die statische Initialisierung. statisch {// etwas tun ...}
deraj
2
Persönlich fühle ich mich mit jeder Form von Interaktivität innerhalb eines statischen Konstruktors unwohl. Ich verstehe Ihr Ziel (lassen Sie jede Methode in dieser statischen Klasse warten, bis der Benutzer autorisiert ist, bevor Sie sie ausführen lassen), aber ich mag diese Methode zur Erreichung dieses Ziels wirklich nicht.
Brian
@Brian: - Ja ... Sie haben Recht ... Ich habe gerade eine Analyse durchgeführt. Schließlich habe ich mich entschieden, keinen Konstruktor zu verwenden, sondern die Methode zu initialisieren
om471987

Antworten:

220

Sie haben hier nur eine Frage gestellt, aber es gibt ungefähr ein Dutzend Fragen, die Sie hätten stellen sollen, also werde ich sie alle beantworten.

Hier ist die Reihenfolge, die ich angenommen habe

  1. Start des Klassenkonstruktors (auch bekannt als cctor)
  2. Ende des Cctors
  3. Start von Main
  4. Start von MyMethod

Ist das richtig?

Nein. Die richtige Reihenfolge ist:

  1. Start von cctor für Programm, falls vorhanden. Da ist nicht.
  2. Ende des Programms für das Programm, falls vorhanden. Da ist nicht.
  3. Start von Main
  4. Start von cctor für MyClass
  5. Ende von cctor für MyClass
  6. Start von MyClass.MyMethod

Was ist, wenn ein statischer Feldinitialisierer vorhanden ist?

Die CLR kann in einigen Fällen die Reihenfolge ändern, in der statische Feldinitialisierer ausgeführt werden. Siehe Jons Seite zu diesem Thema für Details:

Die Unterschiede zwischen statischen Konstruktoren und Typinitialisierern

Ist es jemals möglich, eine statische Methode MyMethodaufzurufen, bevor der Cctor dieser Klasse abgeschlossen ist?

Ja. Wenn der Cctor selbst MyMethod aufruft, wird MyMethod offensichtlich aufgerufen, bevor der Cctor abgeschlossen ist.

Der Cctor ruft MyMethod nicht auf. Ist es jemals möglich, eine statische Methode MyMethodaufzurufen, bevor der Cctor von MyClass abgeschlossen ist?

Ja. Wenn der Cctor einen anderen Typ verwendet, dessen Cctor MyMethod aufruft, wird MyMethod aufgerufen, bevor der MyClass-Cctor abgeschlossen ist.

Kein Arzt ruft MyMethod direkt oder indirekt auf! MyMethodIst es jemals möglich, eine statische Methode aufzurufen, bevor der Cctor von MyClass abgeschlossen ist?

Nein.

Gilt das auch dann noch, wenn mehrere Threads beteiligt sind?

Ja. Der Cctor wird in einem Thread beendet, bevor die statische Methode in einem Thread aufgerufen werden kann.

Kann der Cctor mehrmals aufgerufen werden? Angenommen, zwei Threads bewirken, dass der Cctor ausgeführt wird.

Der Cctor wird garantiert höchstens einmal aufgerufen, egal wie viele Threads beteiligt sind. Wenn zwei Threads MyMethod "gleichzeitig" aufrufen, laufen sie. Einer von ihnen verliert das Rennen und blockt, bis der MyClass-Cctor den Sieger-Thread abgeschlossen hat.

Der verlorene Faden blockiert, bis der Cctor fertig ist? Wirklich ?

Ja wirklich.

Was ist, wenn der Cctor im Gewinner- Thread Code aufruft, der eine Sperre blockiert, die zuvor vom Verlust- Thread übernommen wurde?

Dann haben Sie eine klassische Umkehrbedingung für die Sperrreihenfolge. Ihr Programm blockiert. Für immer.

Das scheint gefährlich. Wie kann ich den Deadlock vermeiden?

Wenn es weh tut, wenn Sie das tun, dann hören Sie damit auf . Tun Sie niemals etwas, das einen Cctor blockieren kann.

Ist es eine gute Idee, sich auf die Semantik der Cctor-Initialisierung zu verlassen, um komplexe Sicherheitsanforderungen durchzusetzen? Und ist es eine gute Idee, einen Cctor zu haben, der Benutzerinteraktionen durchführt?

Weder sind gute Ideen. Mein Rat ist, dass Sie einen anderen Weg finden sollten, um sicherzustellen, dass die sicherheitsrelevanten Voraussetzungen Ihrer Methoden erfüllt sind.

Eric Lippert
quelle
5
Eric, ich bin gespannt, warum Sie in dieser Antwort "statischer Konstruktor" durch "Klassenkonstruktor" oder "cctor" ersetzt haben. Ist es unangemessen, "statischen Konstruktor" zu verwenden, wenn auf einen Cctor verwiesen wird?
Phoog
6
@phoog: Ich wollte meine Terminologie konsequent verwenden, also habe ich die kürzeste ausgewählt. "Statischer Konstruktor" und "Klassenkonstruktor" sind beide in Ordnung. Als Implementierungsdetail wird der statische Konstruktor eines Typs als spezielle Methode mit dem Namen ".cctor" ausgegeben. Daher wird ein solcher Konstruktor häufig als "cctor" bezeichnet. Würde ich in einem formelleren Kontext schreiben, würde ich einen der längeren Begriffe verwenden.
Eric Lippert
@EricLippert Gilt dies auch für nicht statische Klassen mit einem statischen Konstruktor?
Legenden
2
@Legends: Gilt das auch für nicht statische Klassen mit einem statischen Konstruktor? Ja.
Eric Lippert
24

Laut MSDN ist ein statischer Konstruktor:

Ein statischer Konstruktor wird automatisch aufgerufen, um die Klasse zu initialisieren, bevor die erste Instanz erstellt oder auf statische Elemente verwiesen wird.

Der statische Konstruktor wird also aufgerufen, bevor die statische Methode MyClass.MyMethod()aufgerufen wird (vorausgesetzt, er wird natürlich nicht auch während der statischen Konstruktion oder der Initialisierung des statischen Feldes aufgerufen).

Wenn Sie dabei etwas Asynchrones tun static constructor, ist es Ihre Aufgabe, dies zu synchronisieren.

James Michael Hare
quelle
7
Wenn Sie etwas Asynchrones tun, das einen zweiten Thread in einem statischen Konstruktor umfasst, werden Sie in eine Welt voller Schmerzen geraten . Nichts macht Deadlocks schneller. Ein Beispiel finden Sie unter stackoverflow.com/a/8883117/88656 .
Eric Lippert
@Eric: stimmte zu ... Ich würde das nicht tun wollen, war mir aber nicht sicher, was genau er wollte, als MyMethod aufgerufen wurde ...
James Michael Hare
11

Die Nummer 3 ist tatsächlich die Nummer 1: Die statische Initialisierung beginnt erst bei der ersten Verwendung der Klasse, zu der sie gehört.

Es ist möglich, wenn MyMethodvom statischen Konstruktor oder einem statischen Initialisierungsblock aufgerufen wird. Wenn Sie nicht MyMethoddirekt oder indirekt von Ihrem statischen Konstruktor aus aufrufen , sollte es Ihnen gut gehen.

dasblinkenlight
quelle
Ich verstehe nur, dass die staticInitialisierung tatsächlich vor der ersten Verwendung aufgerufen werden kann, abhängig von der Berechtigung zur Optimierung.
James Michael Hare
1
Für einen statischen Konstruktor war das wahr, aber für die statische Initialisierung war mein Punkt. Entschuldigung, vielleicht habe ich nur Nissen für den Ausdruck "statische Initialisierung startet nicht ..." ausgewählt. Dies gilt für statische Konstruktionen. Wenn die Klasse jedoch keinen statischen Konstruktor hat, kann die statische Initialisierung vorher erfolgen.
James Michael Hare
Entschuldigung, ich analysiere wahrscheinlich nur die Redewendung. Im Kontext der Frage ist es absolut richtig, ich habe mir nur Sorgen um diesen Satz als eigenständige Anweisung für die statische Initialisierung im Kontext von Klassen ohne explizite statische Konstruktoren gemacht.
James Michael Hare
@ James: Sie analysieren nicht zu viel - die Terminologie ist hier der entscheidende Unterschied. Statische Konstruktoren sind ein C # -Konzept, während die Typinitialisierung eine .NET-Sache ist. Der Code in einem statischen Konstruktor (C #) wird Teil des Typinitialisierers (.NET). Wann und wie der Typinitialisierer (dh die beforefieldinitSemantik) ausgelöst wird, hängt jedoch davon ab, ob die C # -Klasse über einen statischen Konstruktor verfügt.
LukeH
9

Aus der Dokumentation (Schwerpunkt Mine):

Ein statischer Konstruktor wird automatisch aufgerufen, um die Klasse zu initialisieren, bevor die erste Instanz erstellt oder auf statische Elemente verwiesen wird .

Ken
quelle
2

Sie können garantieren, dass 4 immer nach 2 kommt (wenn Sie keine Instanz Ihrer Klasse mit Ihrer statischen Methode erstellen), dies gilt jedoch nicht für 1 und 3.

Scott Chamberlain
quelle
2

Der statische Konstruktor wird aufgerufen, bevor mymethod ausgeführt wird. Wenn Sie jedoch geschraubt sind, wenn 4 vor 2 aufgerufen wird, empfehle ich Ihnen, Ihr Design zu überdenken. Sollte sowieso keine komplizierten Sachen in einem statischen Konstruktor machen.

Umair
quelle
2

Die CLR garantiert, dass der statische Konstruktor ausgeführt wird, bevor auf statische Elemente zugegriffen wird. Ihr Design stinkt jedoch etwas. Es wäre einfacher, so etwas zu tun:

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

Wenn bei Ihrem Entwurf die Authentifizierung fehlschlägt, können Sie MyMethod nur durch Auslösen einer Ausnahme verhindern.

Phoog
quelle
2

Es wird sichergestellt, dass der Konstruktor einer statischen Klasse aufgerufen wurde, bevor eine ihrer Methoden ausgeführt wird. Beispiel:

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!");
    }
}

Ausgabe:

Drücken Sie Enter

// nach dem Drücken der Eingabetaste

Hallo eingehende ...

Hallo!

Hallo!

haiyyu
quelle
using System; Namespace MyNameSpace {class Program {statische Leere Main (string [] args) {Console.WriteLine ("Entered in main"); Boop.SayHi (); Boop.SayHi (); }} statische Klasse Boop {static Boop () {Console.Read (); Console.WriteLine ("Konstruktorschlüssel eingegeben"); } public static void SayHi () {Console.WriteLine ("Methode heißt"); }}} Ja, dieses Programm gibt ein besseres Verständnis
om471987
Möglicherweise. Posten Sie es das nächste Mal jedoch als Antwort. Es ist dann hilfreicher und sichtbarer.
Haiyyu
1

Hier ist die tatsächliche Reihenfolge, in der die Dinge untergehen:

  1. Start von Main
  2. Start des statischen MyClassKonstruktors
  3. Ende des statischen MyClassKonstruktors
  4. Start von MyMethod
  5. Ende des Main

quelle
0

Oder Sie können im Debugger durchgehen.

Saille
quelle