TLDR: Este é um bug conhecido de longa data. Eu escrevi sobre isso em 2010:
https://blogs.msdn.microsoft.com/ericlippert/2010/01/18/a-definite-assignment-anomaly/
É inofensivo e você pode ignorá-lo com segurança e parabenizar-se por encontrar um bug um tanto obscuro.
Por que o compilador não impõe que Emaildeve ser definitivamente atribuído?
Ah, sim, de certa forma. Apenas tem uma idéia errada de que condição implica que a variável seja definitivamente atribuída, como veremos.
Por que esse código é compilado se a estrutura é criada em um assembly separado, mas não é compilado se a estrutura é definida no assembly existente?
Esse é o ponto crucial do bug. O bug é uma consequência da interseção de como o compilador C # faz a verificação definitiva da atribuição nas estruturas e como o compilador carrega metadados das bibliotecas.
Considere isto:
struct Foo
{
public int x;
public int y;
}
// Yes, public fields are bad, but this is just
// to illustrate the situation.
void M(out Foo f)
{
OK, neste ponto, o que sabemos? fé um alias para uma variável do tipo Foo, portanto, o armazenamento já foi alocado e é definitivamente pelo menos no estado em que saiu do alocador de armazenamento. Se houver um valor colocado na variável pelo chamador, esse valor estará lá.
O que precisamos? Exigimos que fseja definitivamente atribuído em qualquer ponto em que o controle saia Mnormalmente. Então, você esperaria algo como:
void M(out Foo f)
{
f = new Foo();
}
que define f.xe f.ycom seus valores padrão. Mas e isso?
void M(out Foo f)
{
f = new Foo();
f.x = 123;
f.y = 456;
}
Isso também deve estar bem. Mas, e aqui está o kicker, por que precisamos atribuir os valores padrão apenas para impressioná-los um momento depois? O verificador de atribuição definitiva do C # verifica se todos os campos estão atribuídos! Isso é legal:
void M(out Foo f)
{
f.x = 123;
f.y = 456;
}
E por que isso não deveria ser legal? É um tipo de valor. fé uma variável e já contém um valor válido do tipo Foo, então vamos definir os campos e pronto, certo?
Direita. Então, qual é o erro?
O bug que você descobriu é: como uma economia de custos, o compilador C # não carrega os metadados para campos particulares de estruturas que estão nas bibliotecas referenciadas . Esses metadados podem ser enormes e reduziriam a velocidade do compilador para pouquíssimas vitórias para carregar tudo na memória todas as vezes.
E agora você deve poder deduzir a causa do bug que encontrou. Quando o compilador verifica se o parâmetro out está definitivamente atribuído, ele compara o número de campos conhecidos com o número de campos definidos inicialmente e, no seu caso, ele conhece apenas os zero campos públicos porque os metadados do campo privado não foram carregados. . O compilador conclui "zero campos obrigatórios, zero campos inicializados, estamos bem".
Como eu disse, esse bug existe há mais de uma década e pessoas como você ocasionalmente o redescobrem e denunciam. É inofensivo e é improvável que seja consertado, pois consertá-lo tem quase zero benefício, mas um grande custo de desempenho.
E é claro que o bug não se repete para campos privados de estruturas que estão no código-fonte no seu projeto, porque obviamente o compilador já tem informações sobre os campos privados em mãos.