Statiska konstruktorer (programmeringsguide för C#)

En statisk konstruktor används för att initiera statiska data eller för att utföra en viss åtgärd som bara behöver utföras en gång. Den anropas automatiskt innan den första instansen skapas eller om några statiska medlemmar refereras till. En statisk konstruktor anropas högst en gång.

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

Det finns flera åtgärder som ingår i statisk initiering. Dessa åtgärder sker i följande ordning:

  1. Statiska fält är inställda på 0. Körningen utför vanligtvis den här initieringen.
  2. Initiatorer för statiska fält körs. De statiska fältinitierarna i den mest härledda typen körs.
  3. Startvärden för statiska fält av bastyp körs. Initialiserare för statiska fält börjar med direktbasen genom varje bastyp till System.Object.
  4. Alla statiska konstruktorkörningar. Alla statiska konstruktorer, från den ultimata basklassen Object.Object genom varje basklass genom typkörningen. Ordningen för körning av statisk konstruktor har inte angetts. Alla statiska konstruktorer i hierarkin körs dock innan några instanser skapas.

Viktigt!

Det finns ett viktigt undantag till regeln som en statisk konstruktor kör innan någon instans skapas. Om en statisk fältinitierare skapar en instans av typen körs initieraren (inklusive alla anrop till en instanskonstruktor) innan den statiska konstruktorn körs. Detta är vanligast i singleton-mönstret enligt följande exempel:

public class Singleton
{
    // Static field initializer calls instance constructor.
    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;
}

En modulinitierare kan vara ett alternativ till en statisk konstruktor. Mer information finns i specifikationen för modulinitierare.

Kommentarer

Statiska konstruktorer har följande egenskaper:

  • En statisk konstruktor tar inte åtkomstmodifierare eller har parametrar.
  • En klass eller struct kan bara ha en statisk konstruktor.
  • Statiska konstruktorer kan inte ärvas eller överbelastas.
  • En statisk konstruktor kan inte anropas direkt och är endast avsedd att anropas av CLR (Common Language Runtime). Den anropas automatiskt.
  • Användaren har ingen kontroll över när den statiska konstruktorn körs i programmet.
  • En statisk konstruktor anropas automatiskt. Den initierar klassen innan den första instansen skapas eller om några statiska medlemmar som deklareras i den klassen (inte dess basklasser) refereras till. En statisk konstruktor körs före en instanskonstruktor. Om variabelinitierare för statiska fält finns i klassen för den statiska konstruktorn körs de i textordningen där de visas i klassdeklarationen. Initierarna körs omedelbart före den statiska konstruktorn.
  • Om du inte anger en statisk konstruktor för att initiera statiska fält initieras alla statiska fält till standardvärdet enligt standardvärdena för C#-typer.
  • Om en statisk konstruktor utlöser ett undantag anropar inte körningen det en andra gång och typen förblir oinitierad under programdomänens livslängd. Oftast utlöses ett TypeInitializationException undantag när en statisk konstruktor inte kan instansiera en typ eller för ett ohanterat undantag som inträffar inom en statisk konstruktor. För statiska konstruktorer som inte uttryckligen definieras i källkoden kan felsökning kräva kontroll av il-koden (intermediate language).
  • Förekomsten av en statisk konstruktor förhindrar tillägg av typattributet BeforeFieldInit . Detta begränsar körningsoptimering.
  • Ett fält som deklareras som static readonly kan endast tilldelas som en del av deklarationen eller i en statisk konstruktor. När en explicit statisk konstruktor inte krävs initierar du statiska fält vid deklaration i stället för via en statisk konstruktor för bättre körningsoptimering.
  • Körningen anropar en statisk konstruktor högst en gång i en enda programdomän. Det anropet görs i en låst region baserat på klassens specifika typ. Inga extra låsningsmekanismer behövs i en statisk konstruktors brödtext. För att undvika risken för dödlägen ska du inte blockera den aktuella tråden i statiska konstruktorer och initierare. Vänta till exempel inte på uppgifter, trådar, väntehandtag eller händelser, hämta inte lås och kör inte blockering av parallella åtgärder som parallella loopar Parallel.Invoke och parallella LINQ-frågor.

Kommentar

Även om det inte är direkt tillgängligt bör förekomsten av en explicit statisk konstruktor dokumenteras för att hjälpa till med felsökning av initieringsfel.

Användning

  • En vanlig användning av statiska konstruktorer är när klassen använder en loggfil och konstruktorn används för att skriva poster till den här filen.
  • Statiska konstruktorer är också användbara när du skapar omslutningsklasser för ohanterad kod, när konstruktorn kan anropa LoadLibrary metoden.
  • Statiska konstruktorer är också en praktisk plats för att framtvinga körningskontroller på typparametern som inte kan kontrolleras vid kompileringstillfället via typparameterbegränsningar.

Exempel

I det här exemplet har klassen Bus en statisk konstruktor. När den första instansen av Bus skapas (bus1) anropas den statiska konstruktorn för att initiera klassen. Exempelutdata verifierar att den statiska konstruktorn bara körs en gång, även om två instanser av Bus skapas och att den körs innan instanskonstruktorn körs.

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.
*/

Språkspecifikation för C#

Mer information finns i avsnittet Statiska konstruktorer i C#-språkspecifikationen.

Se även