web-dev-qa-db-fra.com

Pourquoi n'utilisez-vous pas la directive "using" en C #?

Les normes de codage existantes sur un grand projet C # incluent une règle selon laquelle tous les noms de type doivent être pleinement qualifiés, interdisant l'emploi de la directive "using". Donc, plutôt que le familier:

using System.Collections.Generic;

.... other stuff ....

List<string> myList = new List<string>();

(Il n'est probablement pas surprenant que var soit également interdit.)

Je me retrouve avec:

System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string>();

Cela représente une augmentation de 134% de la frappe, aucune de ces augmentations ne fournissant des informations utiles. À mon avis, 100% de l'augmentation est du bruit (fouillis) qui en fait entrave la compréhension.

En plus de 30 ans de programmation, j'ai vu une telle norme proposée une ou deux fois, mais jamais mise en œuvre. La logique derrière cela m'échappe. La personne qui impose la norme n'est pas stupide et je ne pense pas qu'il soit malveillant. Ce qui laisse la mauvaise alternative comme seule alternative à moins que je manque quelque chose.

Avez-vous déjà entendu parler d'une telle norme imposée? Si oui, quelle en était la raison? Pouvez-vous penser à des arguments autres que "c'est stupide" ou "tout le monde emploie using" qui pourraient convaincre cette personne de supprimer cette interdiction?

Raisonnement

Les raisons de cette interdiction étaient les suivantes:

  1. Il est difficile de passer la souris sur le nom pour obtenir le type complet. Il est préférable de toujours avoir le type complet visible à tout moment.
  2. Les extraits de code envoyés par e-mail n'ont pas le nom complet et peuvent donc être difficiles à comprendre.
  3. Lors de l'affichage ou de la modification du code en dehors de Visual Studio (Notepad ++, par exemple), il est impossible d'obtenir le nom de type complet.

Je soutiens que les trois cas sont rares, et que faire en sorte que tout le monde paie le prix d'un code encombré et moins compréhensible juste pour accueillir quelques cas rares est erroné.

Les problèmes potentiels de conflits dans les espaces de noms, que je pensais être la principale préoccupation, n'étaient même pas mentionnés. C'est particulièrement surprenant car nous avons un espace de noms, MyCompany.MyProject.Core, ce qui est une idée particulièrement mauvaise. J'ai appris depuis longtemps que nommer n'importe quoi System ou Core en C # est un chemin rapide vers la folie.

Comme d'autres l'ont souligné, les conflits d'espace de noms sont facilement gérés par refactorisation, alias d'espaces de noms ou qualification partielle.

77
Jim Mischel

La question plus large:

Avez-vous déjà entendu parler d'une telle norme imposée? Si oui, quelle en était la raison?

Oui, j'en ai entendu parler, et l'utilisation de noms d'objet pleinement qualifiés empêche les collisions de noms. Bien que rares, lorsqu'ils se produisent, ils peuvent être exceptionnellement épineux à comprendre.


Un exemple: Ce type de scénario est probablement mieux expliqué avec un exemple.

Disons que nous avons deux Lists<T> Appartenant à deux projets distincts.

System.Collections.Generic.List<T>
MyCorp.CustomCollections.Optimized.List<T>

Lorsque nous utilisons le nom d'objet complet, il est clair pour lequel List<T> Est utilisé. Cette clarté se fait évidemment au prix de la verbosité.

Et vous pourriez vous disputer: "Attendez! Personne n'utiliserait jamais deux listes comme ça!" C'est là que je soulignerai le scénario de maintenance.

Vous avez écrit le module Foo pour votre société qui utilise la société approuvée et optimisée List<T>.

using MyCorp.CustomCollections.Optimized;

public class Foo {
    List<object> myList = ...;
}

Plus tard, un nouveau développeur décide d'étendre le travail que vous faites. Ne connaissant pas les normes de l'entreprise, ils mettent à jour le bloc using:

using MyCorp.CustomCollections.Optimized;
using System.Collections.Generic;

Et vous pouvez voir comment les choses vont mal en un rien de temps.

Il devrait être trivial de souligner que vous pourriez avoir deux classes propriétaires du même nom mais dans des espaces de noms différents au sein du même projet. Il ne s'agit donc pas seulement de se heurter à des classes fournies par le framework .NET.

MyCorp.WeightsAndLengths.Measurement();
MyCorp.TimeAndSpace.Measurement();

La réalité:
Maintenant, est-ce que est susceptible de se produire dans la plupart des projets? Non, pas vraiment. Mais lorsque vous travaillez sur un grand projet avec beaucoup d'entrées, vous faites de votre mieux pour minimiser les chances que les choses explosent sur vous.

Les grands projets avec plusieurs équipes contributrices sont une sorte de bête spéciale dans le monde des applications. Les règles qui semblent déraisonnables pour d'autres projets deviennent plus pertinentes en raison des flux d'entrée dans le projet et de la probabilité que ceux qui contribuent n'ont pas lu les directives du projet.

Cela peut également se produire lorsque deux grands projets sont fusionnés. Si les deux projets avaient des classes nommées de manière similaire, vous verrez des collisions lorsque vous commencerez à référencer d'un projet à l'autre. Et les projets peuvent être trop importants pour être refactorisés ou la direction n'approuvera pas les dépenses pour financer le temps consacré à la refactorisation.


Alternatives:
Bien que vous n'ayez pas demandé, il convient de souligner que ce n'est pas une excellente solution au problème. Ce n'est pas une bonne idée de créer des classes qui entreront en collision sans leurs déclarations d'espace de noms.

List<T>, En particulier, doit être traité comme un mot réservé et non pas utilisé comme nom pour vos classes.

De même, les espaces de noms individuels dans le projet doivent s'efforcer d'avoir des noms de classe uniques. Devoir essayer de se rappeler avec quelle Foo() de l'espace de noms vous travaillez est une surcharge mentale qu'il vaut mieux éviter. Autrement dit: avoir MyCorp.Bar.Foo() et MyCorp.Baz.Foo() va faire trébucher vos développeurs et mieux vaut éviter.

Si rien d'autre, vous pouvez utiliser des espaces de noms partiels afin de résoudre l'ambiguïté. Par exemple, si vous ne pouvez absolument pas renommer la classe Foo(), vous pouvez utiliser leurs espaces de noms partiels:

Bar.Foo() 
Baz.Foo()

Raisons spécifiques de votre projet actuel:

Vous avez mis à jour votre question avec les raisons spécifiques qui vous ont été données pour votre projet actuel suivant cette norme. Jetons un coup d'œil à eux et nous nous éloignons vraiment de la piste des lapins.

Il est difficile de passer la souris sur le nom pour obtenir le type complet. Il est préférable de toujours avoir le type complet visible à tout moment.

"Lourd?" Um non. Peut-être ennuyeux. Déplacer quelques onces de plastique pour déplacer un pointeur à l'écran n'est pas lourd . Mais je m'égare.

Ce raisonnement ressemble plus à une dissimulation qu'à autre chose. Offhand, je suppose que les classes au sein de l'application sont faiblement nommées et vous devez vous fier à l'espace de noms afin de glaner la quantité appropriée d'informations sémantiques entourant le nom de la classe.

Ce n'est pas une justification valable pour des noms de classe pleinement qualifiés, c'est peut-être une justification valable pour l'utilisation de noms de classe partiellement qualifiés.

Les extraits de code envoyés par e-mail n'ont pas le nom complet et peuvent donc être difficiles à comprendre.

Ce raisonnement (suite?) Renforce mon soupçon que les classes sont actuellement mal nommées. Encore une fois, avoir de mauvais noms de classe n'est pas une bonne justification pour exiger tout pour utiliser un nom de classe complet. Si le nom de classe est difficile à comprendre, il y a beaucoup plus de mal que ce que les noms de classe pleinement qualifiés peuvent corriger.

Lors de l'affichage ou de la modification du code en dehors de Visual Studio (Notepad ++, par exemple), il est impossible d'obtenir le nom de type complet.

De toutes les raisons, celle-ci m'a presque fait cracher ma boisson. Mais encore une fois, je m'égare.

Je me demande pourquoi l'équipe édite-t-elle ou affiche-t-elle fréquemment du code en dehors de Visual Studio? Et maintenant, nous examinons une justification qui est assez bien orthogonale à ce que les espaces de noms sont censés fournir. Il s'agit d'un argument soutenu par les outils, tandis que les espaces de noms sont là pour fournir une structure organisationnelle au code.

Il semble que le projet que vous possédez souffre de mauvaises conventions de dénomination ainsi que de développeurs qui ne profitent pas de ce que l'outillage peut leur fournir. Et plutôt que de résoudre ces problèmes réels, ils ont tenté de gifler un pansement sur l'un des symptômes et nécessitent des noms de classe pleinement qualifiés. Je pense qu'il est prudent de classer cela comme une approche erronée.

Étant donné qu'il existe des classes mal nommées et en supposant que vous ne pouvez pas refactoriser, la bonne réponse consiste à utiliser Visual Studio IDE à son plein avantage. Envisagez éventuellement d'ajouter un plugin comme VS PowerTools Ensuite, quand je regarde AtrociouslyNamedClass() je peux cliquer sur le nom de la classe, appuyer sur F12 et être amené directement à la définition de la classe afin de mieux comprendre ce qu'il essaie de faire. De même, Je peux cliquer sur Shift-F12 pour trouver toutes les taches dans le code qui souffrent actuellement d'avoir à utiliser AtrociouslyNamedClass().

En ce qui concerne les problèmes d'outillage extérieur - la meilleure chose à faire est de simplement l'arrêter. N'envoyez pas d'extraits d'e-mails s'ils ne comprennent pas immédiatement à quoi ils se réfèrent. N'utilisez pas d'autres outils en dehors de Visual Studio car ces outils n'ont pas l'intelligence entourant le code dont votre équipe a besoin. Notepad ++ est un outil génial, mais il n'est pas conçu pour cette tâche.

Je suis donc d'accord avec votre évaluation concernant les trois justifications spécifiques qui vous ont été présentées. Cela dit, je pense qu'on vous a dit: "Nous avons des problèmes sous-jacents sur ce projet qui ne peuvent pas/ne seront pas résolus et c'est ainsi que nous les avons" corrigés "." Et cela évoque évidemment des problèmes plus profonds au sein de l'équipe qui peuvent vous servir de signaux d'alarme.

73
user53019

J'ai travaillé dans une entreprise où ils avaient de nombreuses classes du même nom, mais dans différentes bibliothèques de classes.

Par exemple,

  • Domaine.Client
  • Legacy.Customer
  • ServiceX.Client
  • ViewModel.Customer
  • Database.Customer

Une mauvaise conception, pourrait-on dire, mais vous savez comment ces choses se développent organiquement, et quelle est l'alternative - renommer toutes les classes pour inclure l'espace de noms? Refactorisation majeure de tout?

Dans tous les cas, il y avait plusieurs endroits où les projets référençant toutes ces différentes bibliothèques devaient utiliser plusieurs versions de la classe.

Avec le using directement en haut, c'était une douleur majeure dans le cul, ReSharper ferait des choses étranges pour essayer de le faire fonctionner, réécrivant les directives using à la volée, vous obtiendrez des erreurs de style client ne pouvant pas être casté en tant que client et vous ne saurez pas quel type était le bon.

Donc, ayant au moins l'espace de noms partiel,

var cust = new Domain.Customer

ou

public void Purchase(ViewModel.Customer customer)

considérablement amélioré la lisibilité du code et rendu explicite quel objet vous vouliez.

Ce n'était pas une norme de codage, ce n'était pas l'espace de noms complet et il n'était pas appliqué à toutes les classes en standard.

17
Ewan

Il ne sert à rien d'être trop verbeux ou trop court: il faut trouver un milieu d'or en étant suffisamment détaillé pour éviter les bugs subtils, tout en évitant d'écrire trop de code.

En C #, un exemple de cela sont les noms des arguments. J'ai rencontré plusieurs fois un problème où j'ai passé des heures à chercher une solution, tandis qu'un style d'écriture plus verbeux pourrait éviter le bogue en premier lieu. Le problème consiste en une erreur dans l'ordre des arguments. Par exemple, cela vous semble-t-il juste?

if (...) throw new ArgumentNullException("name", "The name should be specified.");
if (...) throw new ArgumentException("name", "The name contains forbidden characters.");

À première vue, il semble parfaitement bien, mais il y a un bug. Les signatures des constructeurs des exceptions sont:

public ArgumentException(string message, string paramName)
public ArgumentNullException(string paramName, string message)

Vous avez remarqué l'ordre des arguments?

En adoptant un style plus verbeux où le nom d'un argument est spécifié chaque fois que la méthode accepte deux ou plusieurs arguments du même type , nous évitons l'erreur de se reproduire, et donc:

if (...) throw new ArgumentNullException(paramName: "name", message: "The name should be specified.");
if (...) throw new ArgumentException(paramName: "name", message: "The name contains forbidden characters.");

est évidemment plus long à écrire, mais réduit le temps de débogage.

Poussé à l'extrême, on pourrait forcer à utiliser des arguments nommés partout , y compris dans des méthodes telles que doSomething(int, string) où il n'y a aucun moyen de mélanger l'ordre des paramètres. Cela endommagera probablement le projet à long terme, entraînant beaucoup plus de code sans l'avantage d'empêcher le bogue que j'ai décrit ci-dessus.

Dans votre cas, la motivation derrière la règle "No using" est qu'elle devrait rendre plus difficile l'utilisation du mauvais type, comme MyCompany.Something.List<T> Vs System.Collections.Generic.List<T>.

Personnellement, j'éviterais une telle règle car, à mon humble avis, le coût d'un code difficile à lire est bien supérieur à l'avantage d'un risque légèrement réduit de mauvaise utilisation du mauvais type, mais au moins, il existe une raison valable pour cette règle.

7
Arseni Mourzenko

Les espaces de noms donnent au code une structure hiérarchique, permettant des noms plus courts.

   MyCompany.Headquarters.Team.Leader
   MyCompany.Warehouse.Team.Leader

Si vous autorisez le mot-clé "using", il y a une chaîne d'événements ...

  • Tout le monde utilise vraiment un espace de noms global
    (car ils incluent tous les espaces de noms dont ils pourraient avoir besoin)
  • Les gens commencent à avoir des conflits de noms, ou
  • inquiétude à propos des conflits de noms.

Donc, pour éviter le risque, tout le monde commence à utiliser des noms très longs:

   HeadquartersTeamLeader
   WarehouseTeamLeader

La situation dégénère ...

  • Quelqu'un d'autre a peut-être déjà choisi un nom, vous devez donc en utiliser un plus long.
  • Les noms s'allongent de plus en plus.
  • Les longueurs peuvent dépasser 60 caractères, sans maximum - cela devient ridicule.

J'ai vu cela se produire, je peux donc sympathiser avec les entreprises qui interdisent "l'utilisation".

6
user147272

Le problème avec using est qu'il ajoute par magie à l'espace de noms sans déclaration explicite. C'est beaucoup plus un problème pour le programmeur de maintenance qui rencontre quelque chose comme List<> et sans être familier avec le domaine, ne sait pas quelle clause using l'a introduit.

Un problème similaire se produit dans Java si l'on écrit import Java.util.*; plutôt que import Java.util.List; par exemple; cette dernière déclaration documente le nom qui a été introduit afin que lorsque List<> apparaît plus tard dans la source, son origine est connue à partir de la déclaration import.

1