Les directives "utiliser" devraient-elles être à l'intérieur ou à l'extérieur de l'espace de noms?
j'ai lancé StyleCop sur un code C#, et il continue de rapporter que mes directives using
devraient être à l'intérieur de l'espace de noms.
y a-t-il une raison technique pour placer les directives using
à l'intérieur plutôt qu'à l'extérieur de l'espace de noms?
10 réponses
il y a en fait une différence (subtile) entre les deux. Imaginez que vous ayez le code suivant dans File1.cs:
// File1.cs
using System;
namespace Outer.Inner
{
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
imaginez maintenant que quelqu'un ajoute un autre fichier (File2.cs) au projet qui ressemble à ceci:
// File2.cs
namespace Outer
{
class Math
{
}
}
le compilateur recherche Outer
avant de regarder ces directives using
en dehors de l'espace de noms, donc il trouve Outer.Math
au lieu de System.Math
. Malheureusement (ou peut-être heureusement?), Outer.Math
n'a pas de membre PI
, donc File1 est maintenant cassé.
ceci change si vous mettez le using
dans votre déclaration d'espace de noms, comme suit:
// File1b.cs
namespace Outer.Inner
{
using System;
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
maintenant le compilateur recherche System
avant de chercher Outer
, trouve System.Math
, et tout va bien.
Certains diront que Math
pourrait être un mauvais nom pour une classe définie par l'utilisateur, puisqu'il y a déjà une dans System
; le point ici est juste qu'il est une différence, et il affecte la maintenabilité de votre code.
il est également intéressant de noter ce qui se passe si Foo
est dans l'espace de nom Outer
, plutôt que Outer.Inner
. Dans ce cas, l'ajout de Outer.Math
dans File2 casse File1 peu importe où va le using
. Cela implique que le compilateur recherche l'espace de noms le plus proche avant de regarder n'importe quel using
de la directive.
ce fil a déjà quelques grandes réponses, mais je pense que je peux apporter un peu plus de détails avec cette réponse supplémentaire.
tout d'abord, rappelez-vous qu'une déclaration d'espace de noms avec des périodes, comme:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
...
}
est entièrement équivalent à:
namespace MyCorp
{
namespace TheProduct
{
namespace SomeModule
{
namespace Utilities
{
...
}
}
}
}
si vous le souhaitez, vous pouvez mettre des directives using
sur tous ces niveaux. (Bien sûr, nous voulons avoir using
s dans un seul endroit, mais il serait être légal selon la langue.)
la règle pour résoudre quel type est implicite, peut être vaguement déclaré comme ceci: première recherche le plus interne-" scope "pour une correspondance, si rien n'est trouvé il aller d'un niveau au prochain scope et la recherche là-bas, et ainsi de suite , jusqu'à ce qu'une correspondance est trouvée. Si, à un niveau plus qu'une correspondance est trouvée, si l'un de ces types sont de l'actuelle assemblée, choisir que l'un et émettre un avertissement du compilateur. Sinon, abandonner (erreur de compilation).
maintenant, soyons explicites sur ce que cela signifie Dans un exemple concret avec les deux conventions principales.
(1) avec des utilisations à l'extérieur:
using System;
using System.Collections.Generic;
using System.Linq;
//using MyCorp.TheProduct; <-- uncommenting this would change nothing
using MyCorp.TheProduct.OtherModule;
using MyCorp.TheProduct.OtherModule.Integration;
using ThirdParty;
namespace MyCorp.TheProduct.SomeModule.Utilities
{
class C
{
Ambiguous a;
}
}
dans le cas ci-dessus, pour savoir quel type Ambiguous
est, la recherche va dans cet ordre:
- types imbriqués à l'intérieur de
C
(y compris les types imbriqués hérités) - Types dans l'espace de noms courant
MyCorp.TheProduct.SomeModule.Utilities
- Types dans l'espace de noms
MyCorp.TheProduct.SomeModule
- Types
MyCorp.TheProduct
- Types dans
MyCorp
- Types null espace de noms (namespace global)
-
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
, etThirdParty
l'autre convention:
(2) avec des utilisations à l'intérieur:
namespace MyCorp.TheProduct.SomeModule.Utilities
{
using System;
using System.Collections.Generic;
using System.Linq;
using MyCorp.TheProduct; // MyCorp can be left out; this using is NOT redundant
using MyCorp.TheProduct.OtherModule; // MyCorp.TheProduct can be left out
using MyCorp.TheProduct.OtherModule.Integration; // MyCorp.TheProduct can be left out
using ThirdParty;
class C
{
Ambiguous a;
}
}
maintenant, rechercher le type Ambiguous
va dans cet ordre:
- types imbriqués à l'intérieur de
C
(y compris les types imbriqués hérités) - Types dans l'espace de noms courant
MyCorp.TheProduct.SomeModule.Utilities
-
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
, etThirdParty
- Types dans l'espace de noms
MyCorp.TheProduct.SomeModule
- Types dans
MyCorp
- Types null espace de noms (namespace global)
(notez que MyCorp.TheProduct
faisait partie de " 3."et n'était donc pas nécessaire entre "4."et "5.".)
conclusions
peu importe si vous mettez les utilisations à l'intérieur ou à l'extérieur de la déclaration de l'espace de noms, il y a toujours la possibilité que quelqu'un ajoute plus tard un nouveau type avec un nom identique à l'un des espaces de noms qui ont une priorité plus élevée.
aussi, si un namespace imbriqué a le même nom qu'un type, il peut causer des problèmes.
il est toujours dangereux de déplacer les utilisations d'un endroit à un autre parce que la hiérarchie de recherche les changements, et d'un autre type peut être trouvé. Par conséquent, choisissez une convention et de s'y tenir, de sorte que vous n'aurez jamais à déplacer les utilisations.
modèles de Visual Studio, par défaut, mettre les usings en dehors de de l'espace de noms (par exemple si vous faites VS générer une nouvelle classe dans un nouveau fichier).
un (minuscule) avantage d'avoir des utilisations en dehors de est que vous pouvez alors utiliser les directives d'utilisation pour un attribut global, par exemple [assembly: ComVisible(false)]
au lieu de [assembly: System.Runtime.InteropServices.ComVisible(false)]
.
le mettre à l'intérieur des espaces de noms rend les déclarations locales à cet espace de noms pour le fichier (dans le cas où vous avez plusieurs espaces de noms dans le fichier), mais si vous n'avez qu'un espace de noms par fichier, alors il ne fait pas beaucoup de différence s'ils vont à l'extérieur ou à l'intérieur de l'espace de noms.
using ThisNamespace.IsImported.InAllNamespaces.Here;
namespace Namespace1
{
using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
namespace Namespace2
{
using ThisNamespace.IsImported.InJustNamespace2;
}
}
namespace Namespace3
{
using ThisNamespace.IsImported.InJustNamespace3;
}
selon Hanselman-en utilisant la directive et le chargement de L'assemblage... et d'autres articles il n'y a techniquement aucune différence.
Ma préférence est de les mettre en dehors des espaces de noms.
selon la Documentation de StyleCop:
SA1200: UsingDirectivesMustBePlacedWithinnamespace
Cause Une directive C # using est placée à l'extérieur d'un élément namespace.
Description De La Règle Une violation de cette règle se produit lorsqu'une directive using ou une directive using-alias est placée en dehors d'un élément namespace, à moins que le fichier ne contienne aucun élément namespace.
par exemple, le code suivant entraînerait deux violations de cette règle.
using System;
using Guid = System.Guid;
namespace Microsoft.Sample
{
public class Program
{
}
}
le code suivant, cependant, n'entraînerait aucune violation de cette règle:
namespace Microsoft.Sample
{
using System;
using Guid = System.Guid;
public class Program
{
}
}
ce code compilera proprement, sans aucune erreur de compilation. Toutefois, il n'est pas clair quelle version du type Guid est attribuée. Si la directive using est déplacée à l'intérieur de l'espace de noms, comme indiqué ci-dessous, une erreur de compilation se produira:
namespace Microsoft.Sample
{
using Guid = System.Guid;
public class Guid
{
public Guid(string s)
{
}
}
public class Program
{
public static void Main(string[] args)
{
Guid g = new Guid("hello");
}
}
}
le code échoue sur l'erreur de compilation suivante, trouvée sur la ligne contenant Guid g = new Guid("hello");
CS0576: espace de Noms 'Microsoft.Exemple 'contient une définition en conflit avec l'alias 'Guid'
le code crée un alias pour le système.Le type Guid appelé Guid, et crée aussi son propre type appelé Guid avec une interface de constructeur correspondante. Plus tard, le code crée une instance du type Guid. Pour créer cette instance, le compilateur doit choisir entre les deux différentes définitions de Guid. Lorsque la directive using-alias est placée en dehors de l'élément namespace, le compilateur choisira la définition locale de Guid définie dans l'Espace-nom local, et ignorera complètement la directive using-alias définie en dehors de l'Espace-nom. Cela, malheureusement, n'est pas évident à la lecture du code.
lorsque la directive using-alias est positionnée dans l'espace de noms, cependant, le compilateur doit choisir entre deux différents types de Guid, tous deux définis dans le même espace de noms. Ces deux types fournissent un constructeur correspondant. Le compilateur est incapable de prendre une décision, alors il signale l'erreur du compilateur.
placer la directive User-alias en dehors de l'espace de noms est une mauvaise pratique car elle peut conduire à la confusion dans des situations telles que celle-ci, où il n'est pas évident quelle version du type est réellement utilisée. Cela peut potentiellement conduire à un bogue qui pourrait être difficile à diagnostiquer.
placer les directives using-alias dans l'élément namespace élimine cela comme source de bogues.
- De Plusieurs Espaces De Noms
placer plusieurs éléments namespace dans un seul fichier est généralement une mauvaise idée, mais si et quand cela est fait, c'est une bonne idée de placer toutes les directives using dans chacun des éléments namespace, plutôt que globalement en haut du fichier. Ce sera portée des espaces de bien, et permettra également d'éviter le genre de comportement décrit ci-dessus.
il est important de noter que lorsque le code a été écrit en utilisant des directives placées à l'extérieur de l'Espace-nom, il faut prendre soin de déplacer ces directives à l'intérieur de l'Espace-nom, pour s'assurer que cela ne change pas la sémantique du code. Comme expliqué ci-dessus, placer les directives using-alias dans l'élément namespace permet au compilateur de choisir entre les types conflictuels d'une manière qui n'arrivera pas lorsque les directives sont placées en dehors de l'espace de noms.
comment corriger les Violations Pour corriger une violation de cette règle, Déplacez toutes les directives using et les directives using-alias dans l'élément namespace.
il y a un problème avec le placement en utilisant des déclarations à l'intérieur de l'espace de noms quand vous souhaitez utiliser des alias. L'alias ne bénéficie pas des précédentes déclarations using
et doit être entièrement qualifié.
prendre en considération:
namespace MyNamespace
{
using System;
using MyAlias = System.DateTime;
class MyClass
{
}
}
et:
using System;
namespace MyNamespace
{
using MyAlias = DateTime;
class MyClass
{
}
}
cela peut être particulièrement prononcé si vous avez un pseudonyme à long terme tel que le suivant (qui est comment j'ai trouvé le problème):
using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;
avec using
déclarations à l'intérieur de l'espace de noms, il devient soudainement:
using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
pas joli.
comme Jeppe Stig Nielsen dit , ce fil a déjà de grandes réponses, mais j'ai pensé que cette subtilité plutôt évidente était digne d'être mentionnée aussi.
Les directives using
spécifiées à l'intérieur des espaces de noms peuvent rendre le code plus court puisqu'elles n'ont pas besoin d'être pleinement qualifiées comme lorsqu'elles sont spécifiées à l'extérieur.
l'exemple suivant fonctionne parce que les types Foo
et Bar
sont tous les deux dans le même espace de noms global, Outer
.
présumez le fichier de code Foo.cs :
namespace Outer.Inner
{
class Foo { }
}
Et Bar.cs :
namespace Outer
{
using Outer.Inner;
class Bar
{
public Foo foo;
}
}
qui peut omettre l'espace de nom externe dans la directive using
, en abrégé:
namespace Outer
{
using Inner;
class Bar
{
public Foo foo;
}
}
une autre subtilité que je ne crois pas avoir été couverte par les autres réponses est pour quand vous avez une classe et un namespace avec le même nom.
quand vous avez l'importation dans l'espace de noms, alors il trouvera la classe. Si l'importation est à l'extérieur de l'espace de noms de l'importation sera ignorée et la classe et de l'espace de noms pleinement qualifiés.
//file1.cs
namespace Foo
{
class Foo
{
}
}
//file2.cs
namespace ConsoleApp3
{
using Foo;
class Program
{
static void Main(string[] args)
{
//This will allow you to use the class
Foo test = new Foo();
}
}
}
//file2.cs
using Foo; //Unused and redundant
namespace Bar
{
class Bar
{
Bar()
{
Foo.Foo test = new Foo.Foo();
Foo test = new Foo(); //will give you an error that a namespace is being used like a class.
}
}
}
les raisons techniques sont discutées dans les réponses et je pense qu'il vient aux préférences personnelles à la fin puisque la différence n'est pas que grand et il y a des compromis pour les deux. Le modèle par défaut de Visual Studio pour la création de fichiers .cs
utilise des directives using
en dehors des espaces de noms par exemple
on peut ajuster stylecop pour vérifier les directives using
en dehors des espaces de noms en ajoutant stylecop.json
fichier à la racine du dossier de projet avec le suivant:
{
"$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
"orderingRules": {
"usingDirectivesPlacement": "outsideNamespace"
}
}
}
vous pouvez créer ce fichier de configuration au niveau de la solution et l'ajouter à vos projets en tant que 'fichier de lien existant' pour partager la config à travers tous vos projets aussi.
c'est une meilleure pratique si ceux par défaut en utilisant i.e. " références "utilisés dans votre solution source devraient être en dehors des espaces de noms et ceux qui sont " nouvelle référence ajoutée " est une bonne pratique que vous devriez le mettre à l'intérieur de l'espace de noms. Il s'agit de distinguer les références qui sont ajoutées.