Compartilhar via


Inicializadores de módulo

Observação

Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ela inclui alterações de especificação propostas, juntamente com as informações necessárias durante o design e o desenvolvimento do recurso. Esses artigos são publicados até que as alterações de especificação propostas sejam finalizadas e incorporadas na especificação ECMA atual.

Pode haver algumas divergências entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas LDM (reunião de design de idioma) pertinentes.

Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações.

Problema do especialista: https://github.com/dotnet/csharplang/issues/2608

Resumo

Embora a plataforma .NET tenha um recurso que dá suporte direto à gravação de código de inicialização para o assembly (tecnicamente, o módulo), ele não é exposto em C#. Este é um cenário bastante específico, mas uma vez que você se depara com ele, as soluções parecem ser bastante dolorosas. Há relatos de vários clientes (dentro e fora da Microsoft) lutando com o problema e, sem dúvida, há mais casos não documentados.

Motivação

  • Permitir que as bibliotecas façam uma inicialização única e ansiosa quando carregadas, com sobrecarga mínima e sem que o usuário precise chamar nada explicitamente
  • Um ponto problemático específico das abordagens atuais static do construtor é que o runtime deve fazer verificações adicionais sobre o uso de um tipo com um construtor estático, a fim de decidir se o construtor estático precisa ser executado ou não. Isso adiciona sobrecarga mensurável.
  • Permitir que os geradores de origem executem alguma lógica de inicialização global sem que o usuário precise chamar explicitamente nada

Design detalhado

Um método pode ser designado como um inicializador de módulo decorando-o com um [ModuleInitializer] atributo.

using System;
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class ModuleInitializerAttribute : Attribute { }
}

O atributo pode ser usado assim:

using System.Runtime.CompilerServices;
class C
{
    [ModuleInitializer]
    internal static void M1()
    {
        // ...
    }
}

Alguns requisitos são impostos ao método direcionado com este atributo:

  1. O método deve ser static.
  2. O método deve ser sem parâmetros.
  3. O método deve retornar void.
  4. O método não deve ser genérico ou estar contido em um tipo genérico.
  5. O método deve ser acessível a partir do módulo que o contém.
    • Isso significa que a acessibilidade efetiva do método deve ser internal ou public.
    • Isso também significa que o método não pode ser uma função local.

Quando um ou mais métodos válidos com esse atributo são encontrados em uma compilação, o compilador emitirá um inicializador de módulo que chama cada um dos métodos atribuídos. As chamadas serão emitidas em uma ordem reservada, mas determinística.

Desvantagens

Por que não devemos fazer isso?

  • Talvez as ferramentas de terceiros existentes para "injetar" inicializadores de módulo sejam suficientes para usuários que solicitam esse recurso.

Reuniões de design

8 de abril de 2020