45 votes

Les variables non initialisées en C# sont-elles dangereuses ?

Je suis familier avec la spécification C#, section 5.3 qui dit qu'une variable doit être assignée avant d'être utilisée.

En C et en C++ non géré, cela a du sens car la pile n'est pas effacée et l'emplacement mémoire utilisé pour un pointeur peut être n'importe où (ce qui conduit à un bogue difficile à repérer).

Mais j'ai l'impression qu'il n'y a pas de valeurs réellement "non assignées" autorisées par le runtime. En particulier qu'un type de référence qui n'est pas initialisé aura toujours une valeur nulle, jamais la valeur laissée par une invocation précédente de la méthode ou une valeur aléatoire.

Est-ce exact, ou ai-je supposé à tort qu'un chèque nul est suffisant pendant toutes ces années ? Peut-on avoir des variables vraiment non initialisées en C#, ou est-ce que le CLR s'en occupe et il y a toujours algunos valeur fixée ?

70voto

Eric Lippert Points 300275

J'ai l'impression que le runtime n'autorise pas vraiment de valeurs "non attribuées". En particulier, un type de référence qui n'est pas initialisé aura toujours une valeur nulle, jamais la valeur laissée par une invocation précédente de la méthode ou une valeur aléatoire. Est-ce correct ?

Je constate que personne n'a encore répondu à votre question.

La réponse à la question que vous avez posée est "en quelque sorte".

Comme d'autres l'ont noté, certaines variables (éléments de tableaux, champs, etc.) sont classées comme étant automatiquement "initialement assignées" à leur valeur par défaut (qui est nulle pour les types de référence, zéro pour les types numériques, false pour les bools, et la récursion naturelle pour les structures définies par l'utilisateur).

Certaines variables ne sont pas classées comme initialement assignées ; les variables locales en particulier ne sont pas initialement assignées. Elles doivent être classées par le compilateur comme "définitivement assignées" à tous les points où leur valeurs sont utilisés.

Votre question est donc en fait "est-ce qu'une variable locale qui est classifiée comme non définitivement attribué en fait initialement attribué de la même manière qu'un champ le serait ?" Et la réponse à cette question est oui Dans la pratique, le moteur d'exécution attribue initialement tous les locaux.

Il possède plusieurs propriétés intéressantes. Premièrement, vous pouvez observer dans le débogueur qu'ils sont dans leur état par défaut avant leur première affectation. Deuxièmement, il n'y a aucune chance que le ramasseur d'ordures soit amené à déréférencer un mauvais pointeur simplement parce qu'il restait des ordures sur la pile qui sont maintenant traitées comme une référence gérée. Et ainsi de suite.

Le temps d'exécution est autorisé de laisser l'état initial des locaux comme n'importe quelle ordure qui s'y trouvait si cela peut se faire en toute sécurité. Mais en tant que détail d'implémentation, il ne choisit jamais de le faire. Il met à zéro la mémoire d'une variable locale de manière agressive.

La raison de la règle selon laquelle les locaux doivent être définitivement attribués avant d'être utilisés est donc la suivante. pas pour vous empêcher d'observer l'état non initialisé du local. C'est déjà inobservable parce que le CLR efface agressivement les locales à leurs valeurs par défaut, comme il le fait pour les champs et les éléments de tableau. La raison pour laquelle ceci est illégal en C# est que l'utilisation d'un local non assigné a une forte probabilité d'être un bug. Nous le rendons simplement illégal, et le compilateur vous empêche alors d'avoir un tel bug.

0 votes

J'imagine qu'en plus de cette raison (et c'est une bonne raison, avez-vous déjà vu un CS0165 qui n'était pas un bug ou au moins fragile et peu clair ?) le fait que C# soit pauvre en comportements non définis mais autorisés vous donne plus de liberté pour penser à de tels détails d'implémentation sans vous inquiéter de voir le code écrit pour utiliser le comportement "c'est non défini mais nous savons tous que ça fait..." se casser.

0 votes

@JonHanna : Bien sûr, le modèle "déclaration, affectation conditionnelle, test de valeur". Il est utilisé pour les champs tout le temps. Il n'y a aucune raison pour que ce soit plus fragile ou plus sujet à erreur pour un local que pour un champ. Bien sûr, si le runtime n'impose pas l'effacement des locales, alors soit le compilateur devrait le faire, soit il devrait essayer d'éviter les bogues. Dans ce cas, apparemment le runtime les efface, mais n'est pas obligé de le faire...

0 votes

@jmoreno Conceptuellement, le runtime ne les efface pas. La réponse d'Eric ci-dessus nous dit qu'il le fait vraiment, mais bien que ce soit intéressant, ce n'est pas une information pratique pour les appelants (je ne peux même pas penser à un moyen de l'utiliser dans une optimisation, et vous ?) Le fait que l'assignation conditionnelle ne soit pas fragile est dû au fait que nous n'avons généralement pas d'assignations entièrement conditionnelles. Par exemple, tout out doit définir la valeur à quelque chose, à moins qu'il ne lève une exception. Cette règle est strictement un gaspillage - si nous avons reçu false alors nous ne devrions pas utiliser la valeur - mais supprime la fragilité qui existerait autrement.

9voto

Joe Points 18328

Pour autant que je sache, chaque type a une valeur par défaut désignée.

Conformément à ce document, les champs des classes se voient attribuer la valeur par défaut.

http://msdn.microsoft.com/en-us/library/aa645756(v=vs.71).aspx

Ce document indique que les éléments suivants ont toujours des valeurs par défaut attribuées automatiquement.

  • Variables statiques.
  • Variables d'instance des instances de la classe.
  • Variables d'instance des variables struct initialement attribuées.
  • Éléments du tableau.
  • Paramètres de valeur.
  • Paramètres de référence.
  • Variables déclarées dans une clause catch ou une instruction foreach.

http://msdn.microsoft.com/en-us/library/aa691173(v=vs.71).aspx

Plus d'informations sur les valeurs par défaut réelles ici : Valeurs par défaut des types C# (référence C#)

0 votes

Les deux premiers liens sont à moitié rompus (ils mènent à "Visual Studio 2003 Retired Technical documentation" ).

5voto

Tim B Points 1463

Cela dépend de l'endroit où la variable est déclarée. Les variables déclarées dans une classe sont automatiquement initialisées à l'aide de la valeur par défaut.

object o;
void Method()
{
    if (o == null)
    {
        // This will execute
    }
}

Les variables déclarées dans une méthode ne sont pas initialisées, mais lorsque la variable est utilisée pour la première fois, le compilateur vérifie qu'elle a été initialisée, de sorte que le code ne compile pas.

void Method()
{
    object o;
    if (o == null) // Compile error on this line
    {
    }
}

3voto

dasblinkenlight Points 264350

En particulier, un type de référence qui n'est pas initialisé aura toujours une valeur nulle.

Je pense que vous mélangez les variables locales et les variables membres. La section 5.3 parle spécifiquement des variables locales. À la différence des variables membres, qui sont définies par défaut, les variables locales ne sont jamais définies par défaut sur la valeur nulle ou sur quoi que ce soit d'autre. doit être attribués avant leur première lecture. La section 5.3 explique les règles que le compilateur utilise pour déterminer si une variable locale a été assignée ou non.

1voto

Gabe Points 49718

Il existe trois façons d'attribuer une valeur initiale à une variable :

  1. Par défaut -- cela se produit (par exemple) si vous déclarez une variable de classe sans lui attribuer une valeur initiale, de sorte que la valeur initiale obtient default(type)type est le type que vous avez déclaré pour la variable.

  2. Avec un initialisateur - cela se produit lorsque vous déclarez une variable avec une valeur initiale, comme dans int i = 12;

  3. Tout point avant que sa valeur ne soit récupérée -- cela se produit (par exemple) si vous avez une variable locale sans valeur initiale. Le compilateur s'assure que vous n'avez aucun chemin de code accessible qui lira la valeur de la variable avant qu'elle ne soit assignée.

À aucun moment le compilateur ne vous permettra de lire la valeur d'une variable qui n'a pas été initialisée, de sorte que vous ne devez jamais vous inquiéter de ce qui se passerait si vous essayiez.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X