J'ai remarqué un comportement bizarre dans mon code lors du commentaire accidentel d'une ligne dans une fonction lors de la révision du code. C'était très difficile à reproduire mais je vais illustrer un exemple similaire ici.
J'ai cette classe de test:
public class Test
{
public void GetOut(out EmailAddress email)
{
try
{
Foo(email);
}
catch
{
}
}
public void Foo(EmailAddress email)
{
}
}
il n'y a pas d'affectation à la messagerie électronique dans GetOut
laquelle cela générerait normalement une erreur:
Le paramètre de sortie 'email' doit être affecté à avant que le contrôle ne quitte la méthode actuelle
Cependant, si EmailAddress est dans une structure dans un assembly séparé, aucune erreur n'est créée et tout se compile correctement.
public struct EmailAddress
{
#region Constructors
public EmailAddress(string email)
: this(email, string.Empty)
{
}
public EmailAddress(string email, string name)
{
this.Email = email;
this.Name = name;
}
#endregion
#region Properties
public string Email { get; private set; }
public string Name { get; private set; }
#endregion
}
Pourquoi le compilateur n'applique-t-il pas qu'Email doit être assigné? 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?
struct Dog{}
, tout va bien.Réponses:
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.
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.
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:
OK, à ce stade, que savons-nous?
f
est un alias pour une variable de typeFoo
, 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 partM
normalement. Vous vous attendez donc à quelque chose comme:qui définit
f.x
etf.y
à leurs valeurs par défaut. Mais qu'en est-il?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:
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 valideFoo
, 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.
la source
System.TimeSpan
place, les erreurs surviennent:error CS0269: Use of unassigned out parameter 'email'
eterror CS0177: The out parameter 'email' must be assigned to before control leaves the current method
. Il n'y a qu'un seul champ non statique deTimeSpan
, à savoir_ticks
. C'estinternal
à son assemblage mscorlib. Cet assemblage est-il spécial? Idem avecSystem.DateTime
, et son domaine estprivate
Bien que cela ressemble à un bug, cela a du sens.
L''erreur manquante 'n'apparaît que lors de l'utilisation d'une bibliothèque de classes. Et une bibliothèque de classes peut avoir été écrite dans un autre langage .net, par exemple VB.Net. Le `` suivi d'affectation défini '' est une fonctionnalité de C #, pas du cadre.
Donc, dans l'ensemble, je ne pense pas que ce soit un bug, mais je ne connais pas de déclaration faisant autorité pour cela.
la source
default(T)
). Il n'y a donc aucune violation de la sécurité de la mémoire ou quelque chose de similaire.