C'est une question soulevée par l'un des membres de notre équipe lors du processus de programmation. Parce que cette erreur de compilation est facile à éviter, je n'ai jamais réfléchi attentivement à ce problème jusqu'à ce que je regarde son code et réalise que ce problème n'est pas si simple.
Jetez d'abord un œil à ce code :
code
Programme de classe
{
static void Main(string[] arguments)
{
octet[] buf = nouvel octet[1024];
T t = nouveau T();
chaîne str = "1234" ;
entier n = 1234 ;
int?nn = 1234;
DateTime dt = DateTime.Maintenant ;
objet o = 1234 ;
Console.WriteLine("terminer");
}
}
classe T { }
Selon vous, combien de variables sont inutilisées dans ce code ?
Si vous regardez les choses du point de vue d'un programmeur, la réponse devrait être que toutes les variables sont inutilisées. Mais le résultat donné par le compilateur est un peu contre-intuitif :
La variable "str" a reçu une valeur, mais sa valeur n'a jamais été utilisée. La variable "n" a reçu une valeur, mais sa valeur n'a jamais été utilisée. La variable "nn" a reçu une valeur. la valeur n’a jamais été utilisée.
Ce qui est étrange, c'est que même si toutes les variables sont déclarées de la même manière, le compilateur pense seulement que certaines d'entre elles sont inutilisées. Que se passe-t-il?
Analysons-les un par un. Regardez d'abord le tableau Si la valeur par défaut est utilisée, les informations fournies par le compilateur sont différentes :
byte[] buf1 = null; // Il y a un avertissement
byte[] buf2 = new byte[1024]; // aucun avertissement
Ce résultat semble indiquer que si l'affectation du paramètre est nulle, le compilateur n'effectuera pas réellement l'affectation et la variable sera traitée comme si elle n'avait pas été utilisée. Les résultats de l'inspection IL peuvent également prouver cette affirmation : pour la première ligne, le compilateur n'a généré aucune instruction correspondante pour la deuxième ligne, l'instruction newattr a été utilisée pour créer le tableau ;
Pour les cours personnalisés :
T t1 = null ; // Il y a un avertissement
T t2 = new T(); // aucun avertissement
Ce résultat devrait être compréhensible (même s’il est compréhensible, je ne pense pas qu’il soit bon, pour les raisons indiquées ci-dessous). Bien que nous n'appelions aucune méthode de la classe, le constructeur de la classe peut toujours effectuer certaines opérations, donc tant qu'une classe est créée, le compilateur la traitera comme si elle avait été utilisée.
Pour les types valeur de base, leur comportement est différent de celui des types référence. Le compilateur ne traite pas l'affectation initiale comme une utilisation de variables :
int n1 = 0; // Il y a un avertissement
int n2 = 1234; // Il y a un avertissement
int? n3 = null; // Il y a un avertissement
int? n4 = 0; // Il y a un avertissement
int? n5 = 1234; // Il y a un avertissement
String doit être considéré comme un type de référence en termes d'implémentation, mais ses performances sont plus similaires à celles d'un type valeur et les informations d'avertissement sont les mêmes que celles d'un type valeur.
Pour les types de valeurs légèrement plus complexes, les résultats sont un peu plus subtils :
DateTime dt1 ; // Il y a un avertissement
DateTime dt2 = new DateTime(); // Il y a un avertissement
DateTime dt3 = new DateTime(2009,1,1); // aucun avertissement
DateTime dt4 = DateTime.Now ; // aucun avertissement
Il y a une chose à noter à propos de ce résultat. Bien que le constructeur par défaut et le constructeur paramétré de DateTime soient tous deux des constructeurs du point de vue de l'utilisateur, ils sont différents du point de vue du compilateur. On peut également voir en décompilant avec IL que si le constructeur par défaut est appelé, le compilateur appelle l'instruction initobj, tandis que l'instruction call ctor est utilisée pour le constructeur paramétré. De plus, bien que le format du code d'affectation soit exactement le même du point de vue du programmeur, le compilateur adoptera des stratégies de construction différentes en fonction de la valeur attribuée, ce qui est également contre-intuitif.
La conclusion finale est regrettable, c'est-à-dire que les avertissements de compilation de C# ne suffisent pas à donner aux programmeurs une protection suffisante, notamment pour les tableaux :
octet[] buf = nouvel octet[1024];
Si vous construisez uniquement un tel tableau sans l’utiliser, le compilateur ne donnera aucun message d’avertissement au programmeur.
Un autre problème à considérer est de déclarer une classe sans utiliser aucune méthode, comme simplement
T t = nouveau T()
Est-ce un comportement raisonnable ? Le compilateur devrait-il émettre un avertissement à ce sujet ?
Mon opinion personnelle est que du point de vue de l'utilisation, cela est déraisonnable et doit être évité autant que possible. Le compilateur devrait émettre un avertissement s'il découvre cette utilisation. Si nécessaire, vous pouvez éviter les messages d'avertissement en le déclarant spécifiquement via des directives de compilation ou des méthodes d'attribut. Cependant, le comportement du compilateur C# n'est pas d'émettre des avertissements, ce avec quoi je ne suis pas d'accord. Bien sûr, j’espère aussi que chacun proposera ses propres idées.