O tipo bool tem um histórico quadriculado com muitas opções incompatíveis entre os tempos de execução do idioma. Isso começou com uma escolha histórica de design feita por Dennis Ritchie, o cara que inventou a linguagem C. Não tinha um tipo booleano , a alternativa era int em que um valor 0 representa false e qualquer outro valor foi considerado verdadeiro .
Essa opção foi levada adiante no Winapi, o principal motivo para usar o pinvoke, pois possui um typedef para o BOOL
qual é um alias da palavra-chave int do compilador C. Se você não aplicar um atributo [MarshalAs] explícito, um booleano C # será convertido em BOOL, produzindo um campo com 4 bytes de comprimento.
Faça o que fizer, sua declaração de estrutura precisa corresponder à escolha do tempo de execução feita no idioma com o qual você interage. Como observado, o BOOL para o winapi, mas a maioria das implementações de C ++ escolheu o byte , a maior interoperabilidade de automação do COM usa VARIANT_BOOL, que é curto .
O tamanho real de um C # bool
é um byte. Um forte objetivo de design do CLR é que você não pode descobrir. O layout é um detalhe de implementação que depende muito do processador. Os processadores são muito exigentes quanto aos tipos de variáveis e alinhamento; escolhas erradas podem afetar significativamente o desempenho e causar erros de tempo de execução. Ao tornar o layout não detectável, o .NET pode fornecer um sistema de tipo universal que não depende da implementação real do tempo de execução.
Em outras palavras, você sempre precisa organizar uma estrutura em tempo de execução para definir o layout. Nesse momento, é feita a conversão do layout interno para o layout de interoperabilidade. Isso pode ser muito rápido se o layout for idêntico, lento quando os campos precisarem ser reorganizados, pois isso sempre exige a criação de uma cópia da estrutura. O termo técnico para isso é blittable , passar uma estrutura blittable para o código nativo é rápido porque o empacotador pinvoke pode simplesmente passar um ponteiro.
O desempenho também é a principal razão pela qual um bool não é um único bit. Existem poucos processadores que tornam um pouco diretamente endereçável, a menor unidade é um byte. É necessária uma instrução extra para pescar o bit fora do byte, que não é de graça. E nunca é atômico.
O compilador C # não tem vergonha de dizer que é necessário 1 byte, use sizeof(bool)
. Ainda não é um preditor fantástico para quantos bytes um campo leva em tempo de execução, o CLR também precisa implementar o modelo de memória .NET e promete que atualizações simples de variáveis sejam atômicas . Isso requer que as variáveis sejam alinhadas corretamente na memória para que o processador possa atualizá-lo com um único ciclo de barramento de memória. Com bastante frequência, um bool exige 4 ou 8 bytes de memória por causa disso. Preenchimento extra que foi adicionado para garantir que o próximo membro esteja alinhado corretamente.
O CLR realmente aproveita o fato de o layout não ser descoberto, ele pode otimizar o layout de uma classe e reorganizar os campos para que o preenchimento seja minimizado. Então, digamos, se você tem uma classe com um membro bool + int + bool, seriam necessários 1 + (3) + 4 + 1 + (3) bytes de memória, (3) é o preenchimento, para um total de 12 bytes. 50% de resíduos. O layout automático é reorganizado para 1 + 1 + (2) + 4 = 8 bytes. Somente uma classe tem layout automático, as estruturas têm layout seqüencial por padrão.
Mais sombriamente, um bool pode exigir até 32 bytes em um programa C ++ compilado com um compilador C ++ moderno que suporta o conjunto de instruções AVX. O que impõe um requisito de alinhamento de 32 bytes, a variável bool pode acabar com 31 bytes de preenchimento. Além disso, a principal razão pela qual uma instabilidade .NET não emite instruções SIMD, a menos que explicitamente agrupada, não pode obter a garantia de alinhamento.