TLDR: Il s'agit d'un bug connu de longue date. J'ai écrit pour la première fois à ce sujet en 2010:
https://blogs.msdn.microsoft.com/ericlippert/2010/01/18/a-definite-assignment-anomaly/
Il est inoffensif et vous pouvez l'ignorer en toute sécurité, et félicitez-vous d'avoir trouvé un bug quelque peu obscur.
Pourquoi le compilateur n'applique-t- Email
il pas une attribution définitive?
Oh, c'est le cas, d'une manière. Il a juste une mauvaise idée de quelle condition implique que la variable est définitivement assignée, comme nous le verrons.
Pourquoi ce code compile-t-il si la structure est créée dans un assembly séparé, mais il ne compile pas si la structure est définie dans l'assembly existant?
C'est le noeud du bug. Le bogue est une conséquence de l'intersection de la façon dont le compilateur C # vérifie l'affectation définie sur les structures et comment le compilateur charge les métadonnées à partir des bibliothèques.
Considère ceci:
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, à ce stade, que savons-nous? f
est un alias pour une variable de type Foo
, donc le stockage a déjà été alloué et est définitivement au moins dans l'état où il est sorti de l'allocateur de stockage. S'il y avait une valeur placée dans la variable par l'appelant, cette valeur est là.
De quoi avons-nous besoin? Nous exigeons que cela f
soit définitivement attribué à tout moment où le contrôle part M
normalement. Vous vous attendez donc à quelque chose comme:
void M(out Foo f)
{
f = new Foo();
}
qui définit f.x
et f.y
à leurs valeurs par défaut. Mais qu'en est-il?
void M(out Foo f)
{
f = new Foo();
f.x = 123;
f.y = 456;
}
Cela devrait aussi être bien. Mais, et voici le kicker, pourquoi devons-nous attribuer les valeurs par défaut uniquement pour les éliminer un instant plus tard? Le vérificateur d'affectation définitive de C # vérifie si chaque champ est affecté! C'est légal:
void M(out Foo f)
{
f.x = 123;
f.y = 456;
}
Et pourquoi cela ne devrait-il pas être légal? C'est un type de valeur. f
est une variable, et elle contient déjà une valeur de type valide Foo
, nous allons donc définir les champs, et nous avons terminé, non?
Droite. Alors quel est le bug?
Le bogue que vous avez découvert est: pour réduire les coûts, le compilateur C # ne charge pas les métadonnées pour les champs privés des structures qui se trouvent dans les bibliothèques référencées . Ces métadonnées peuvent être énormes , et cela ralentirait le compilateur pour très peu de gain pour tout charger en mémoire à chaque fois.
Et maintenant, vous devriez pouvoir déduire la cause du bug que vous avez trouvé. Lorsque le compilateur vérifie si le paramètre out est définitivement attribué, il compare le nombre de champs connus au nombre de champs qui ont été définitivement initialisés et, dans votre cas, il ne connaît que les champs publics zéro car les métadonnées de champ privé n'ont pas été chargées . Le compilateur conclut "zéro champs requis, zéro champs initialisés, nous sommes bons."
Comme je l'ai dit, ce bug existe depuis plus d'une décennie et des gens comme vous le redécouvrent occasionnellement et le signalent. Il est inoffensif et il est peu probable qu'il soit réparé car sa réparation présente un avantage presque nul mais un coût de performance élevé.
Et bien sûr, le bogue ne fait pas de reproches pour les champs privés de structures qui sont en code source dans votre projet, car évidemment le compilateur a déjà des informations sur les champs privés à portée de main.