Partilhar via


Construtores estáticos (Guia de programação em C#)

Um construtor estático é usado para inicializar quaisquer dados estáticos ou para executar uma ação específica que precisa ser executada apenas uma vez. Ele é chamado automaticamente antes que a primeira instância seja criada ou quaisquer membros estáticos sejam referenciados. Um construtor estático é chamado no máximo uma vez.

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

Há várias ações que fazem parte da inicialização estática. Essas ações têm lugar pela seguinte ordem:

  1. Os campos estáticos são definidos como 0. O tempo de execução normalmente faz essa inicialização.
  2. Os inicializadores de campo estático são executados. Os inicializadores de campo estático no tipo mais derivado são executados.
  3. Os inicializadores de campo estático do tipo base são executados. Inicializadores de campo estático começando com a base direta através de cada tipo de base para System.Object.
  4. Qualquer construtor estático é executado. Qualquer construtor estático, desde a classe base final de através de Object.Object cada classe base até o tipo run. A ordem de execução do construtor estático não é especificada. No entanto, todos os construtores estáticos na hierarquia são executados antes de qualquer instância ser criada.

Importante

Há uma exceção importante à regra de que um construtor estático é executado antes de qualquer instância ser criada. Se um inicializador de campo estático cria uma instância do tipo, esse inicializador é executado (incluindo qualquer chamada para um construtor de instância) antes que o construtor estático seja executado. Isso é mais comum no padrão singleton, conforme mostrado no exemplo a seguir:

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

Um inicializador de módulo pode ser uma alternativa a um construtor estático. Para obter mais informações, consulte a especificação para inicializadores de módulo.

Observações

Os construtores estáticos têm as seguintes propriedades:

  • Um construtor estático não usa modificadores de acesso ou tem parâmetros.
  • Uma classe ou struct só pode ter um construtor estático.
  • Os construtores estáticos não podem ser herdados ou sobrecarregados.
  • Um construtor estático não pode ser chamado diretamente e destina-se apenas a ser chamado pelo Common Language Runtime (CLR). É invocado automaticamente.
  • O usuário não tem controle sobre quando o construtor estático é executado no programa.
  • Um construtor estático é chamado automaticamente. Ele inicializa a classe antes que a primeira instância seja criada ou quaisquer membros estáticos declarados nessa classe (não suas classes base) sejam referenciados. Um construtor estático é executado antes de um construtor de instância. Se inicializadores de variáveis de campo estático estiverem presentes na classe do construtor estático, eles serão executados na ordem textual em que aparecem na declaração de classe. Os inicializadores são executados imediatamente antes do construtor estático.
  • Se você não fornecer um construtor estático para inicializar campos estáticos, todos os campos estáticos serão inicializados com seu valor padrão, conforme listado em Valores padrão de tipos C#.
  • Se um construtor estático lançar uma exceção, o tempo de execução não a invocará uma segunda vez e o tipo permanecerá não inicializado durante o tempo de vida do domínio do aplicativo. Mais comumente, uma TypeInitializationException exceção é lançada quando um construtor estático é incapaz de instanciar um tipo ou para uma exceção não tratada que ocorre dentro de um construtor estático. Para construtores estáticos que não estão explicitamente definidos no código-fonte, a solução de problemas pode exigir a inspeção do código de linguagem intermediária (IL).
  • A presença de um construtor estático impede a BeforeFieldInit adição do atributo type. Isso limita a otimização do tempo de execução.
  • Um campo declarado como static readonly só pode ser atribuído como parte de sua declaração ou em um construtor estático. Quando um construtor estático explícito não for necessário, inicialize campos estáticos na declaração em vez de através de um construtor estático para melhor otimização do tempo de execução.
  • O tempo de execução chama um construtor estático não mais do que uma vez em um único domínio de aplicativo. Essa chamada é feita em uma região bloqueada com base no tipo específico da classe. Não são necessários mecanismos de bloqueio adicionais no corpo de um construtor estático. Para evitar o risco de deadlocks, não bloqueie o thread atual em construtores e inicializadores estáticos. Por exemplo, não espere em tarefas, threads, identificadores de espera ou eventos, não adquira bloqueios e não execute operações paralelas de bloqueio, Parallel.Invoke como loops paralelos e consultas LINQ paralelas.

Nota

Embora não seja diretamente acessível, a presença de um construtor estático explícito deve ser documentada para ajudar na solução de problemas de exceções de inicialização.

Utilização

  • Um uso típico de construtores estáticos é quando a classe está usando um arquivo de log e o construtor é usado para gravar entradas nesse arquivo.
  • Construtores estáticos também são úteis ao criar classes wrapper para código não gerenciado, quando o construtor pode chamar o LoadLibrary método.
  • Os construtores estáticos também são um lugar conveniente para impor verificações de tempo de execução no parâmetro type que não podem ser verificadas em tempo de compilação por meio de restrições de parâmetro de tipo.

Exemplo

Neste exemplo, a classe Bus tem um construtor estático. Quando a primeira instância de é criada (bus1), o construtor estático é invocado Bus para inicializar a classe. A saída de exemplo verifica se o construtor estático é executado apenas uma vez, mesmo que duas instâncias de são criadas, e que ele é executado antes que o construtor de Bus instância seja executado.

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

Especificação da linguagem C#

Para obter mais informações, consulte a seção Construtores estáticos da especificação da linguagem C#.

Consulte também