Como funciona um construtor estático?
-
29-10-2019 - |
Pergunta
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();
}
}
}
Aqui está a sequência que assumi
- Início do construtor estático
- Fim do construtor estático
- Início do principal
- Início do meu método
- Fim do principal
Agora, em qualquer cenário, se 4 começar antes de 2, estou ferrado.É possível?
Solução
Você fez apenas uma pergunta aqui, mas há cerca de uma dúzia de perguntas que você deveria ter feito, então responderei a todas.
Aqui está a sequência que assumi
- Início do construtor de classe (também conhecido como
cctor
) - Fim do cctor
- início do principal
- início de MyMethod
Isso está correto?
Não. A sequência correta é:
- Início do cctor para programa, se houver. Não existe.
- Fim do cctor para o programa, se houver. Não existe.
- Início do principal
- Início do cctor para MyClass
- Fim do cctor para MyClass
- Início de MyClass.MyMethod
E se houver um inicializador de campo estático?
O CLR tem permissão para alterar a ordem em que os inicializadores de campo estático são executados em alguns casos. Veja a página de Jon sobre o assunto para detalhes:
As diferenças entre construtores estáticos e inicializadores de tipo
É possível que um método estático como
MyMethod
seja chamado antes que o cctor dessa classe seja concluído?
Sim. Se o próprio cctor chamar MyMethod, obviamente, MyMethod será chamado antes que o cctor seja concluído.
O cctor não chama MyMethod. É possível que um método estático como
MyMethod
seja chamado antes que o cctor de MyClass seja concluído?
Sim. Se o cctor usar outro tipo cujo cctor chama MyMethod, MyMethod será chamado antes que o cctor MyClass seja concluído.
Nenhum cctor chama MyMethod, direta ou indiretamente! Agora é possível para um método estático como
MyMethod
ser chamado antes que o cctor de MyClass seja concluído?
Não.
Isso ainda é verdade mesmo se houver vários tópicos envolvidos?
Sim. O cctor terminará em um thread antes que o método estático possa ser chamado em qualquer thread.
O cctor pode ser chamado mais de uma vez? Suponha que dois threads façam com que o cctor seja executado.
O cctor tem garantia de ser chamado no máximo uma vez, não importa quantos tópicos estejam envolvidos. Se dois threads chamam MyMethod "ao mesmo tempo", eles disparam. Um deles perde a corrida e bloqueia até que o cctor MyClass termine no tópico vencedor.
O segmento perdedor bloqueia até que o cctor termine? Sério ?
Sério.
E daí se o cctor no encadeamento vencedor chamar o código que bloqueia em um bloqueio anteriormente realizado pelo encadeamento perdedor ?
Então você tem uma condição clássica de inversão de ordem de bloqueio. Seu programa bloqueia. Para sempre.
Isso parece perigoso. Como posso evitar o impasse?
Se dói quando você faz isso, pare de fazer isso . Nunca faça algo que possa bloquear um cctor.
É uma boa ideia confiar na semântica de inicialização do cctor para impor requisitos de segurança complexos? E é uma boa ideia ter um cctor que interaja com o usuário?
Nem são boas ideias. Meu conselho é que você deve encontrar uma maneira diferente de garantir que as pré-condições que afetam a segurança de seus métodos sejam atendidas.
Outras dicas
De acordo com o MSDN , um construtor estático:
Um construtor estático é chamado automaticamente para inicializar a classe antes que a primeira instância seja criada ou qualquer membro estático seja referenciado.
Portanto, o construtor estático será chamado antes que o método estático MyClass.MyMethod()
seja chamado (assumindo que não também chamado durante a construção estática ou inicialização de campo estático, é claro).
Agora, se você está fazendo algo assíncrono nesse static constructor
, é seu trabalho sincronizá-lo.
O nº 3 é, na verdade, o nº 1: a inicialização estática não começa até o primeiro uso da classe à qual pertence.
É possível se MyMethod
for chamado a partir do construtor estático ou de um bloco de inicialização estático.Se você não invocar MyMethod
direta ou indiretamente de seu construtor estático, tudo bem.
Da documentação (ênfase minha):
Um construtor estático é chamado automaticamente para inicializar a classe antes que a primeira instância seja criada ou quaisquer membros estáticos sejam referenciado .
Você pode garantir que 4 sempre virá após 2 (se você não criar uma instância de sua classe a partir de seu método estático), no entanto, o mesmo não é verdade para 1 e 3.
O construtor estático será chamado antes de meu método ser executado.No entanto, se você está ferrado se 4 for chamado antes de 2, então sugiro que você repense seu design.Não deveria estar fazendo coisas complicadas em um construtor estático de qualquer maneira.
O CLR garante que o construtor estático seja executado antes que qualquer membro estático seja acessado.No entanto, seu design é um pouco malcheiroso.Seria mais simples fazer algo assim:
static void Main(string[] args)
{
bool userIsAuthenticated = MyClass.AuthenticateUser();
if (userIsAuthenticated)
MyClass.MyMethod();
}
Com o seu design, se a autenticação falhar, a única maneira de impedir a execução de MyMethod é lançando uma exceção.
É garantido que um construtor de classe estática foi chamado antes que qualquer um de seus métodos fosse executado.Exemplo:
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!");
}
}
Resultado:
Pressione Enter
// depois de pressionar enter
Olá, entrando ...
Olá!
Olá!
Esta é a ordem real em que as coisas acontecem:
- Início do
Main
- Início do construtor estático
MyClass
- Fim do construtor estático
MyClass
- Início do
MyMethod
- Fim do
Main
Ou você pode avançar no depurador.