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?

1770
demandé sur Philippe 2008-09-24 07:49:50

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.

1864
répondu Charlie 2018-04-13 11:46:23

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:

  1. types imbriqués à l'intérieur de C (y compris les types imbriqués hérités)
  2. Types dans l'espace de noms courant MyCorp.TheProduct.SomeModule.Utilities
  3. Types dans l'espace de noms MyCorp.TheProduct.SomeModule
  4. Types MyCorp.TheProduct
  5. Types dans MyCorp
  6. Types null espace de noms (namespace global)
  7. System , System.Collections.Generic , System.Linq , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration , et ThirdParty

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:

  1. types imbriqués à l'intérieur de C (y compris les types imbriqués hérités)
  2. Types dans l'espace de noms courant MyCorp.TheProduct.SomeModule.Utilities
  3. System , System.Collections.Generic , System.Linq , MyCorp.TheProduct , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration , et ThirdParty
  4. Types dans l'espace de noms MyCorp.TheProduct.SomeModule
  5. Types dans MyCorp
  6. 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)] .

354
répondu Jeppe Stig Nielsen 2013-11-08 09:57:51

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;
}
182
répondu Mark Cidade 2016-04-17 14:49:14

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.

58
répondu Quintin Robinson 2012-10-22 14:46:04

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.

  1. 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.

45
répondu JaredCacurak 2009-09-14 15:17:10

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.

31
répondu Neo 2016-03-24 01:57:00

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;
    }
}
3
répondu Biscuits 2017-05-23 12:26:32

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.
        }
    }
}
1
répondu Ben Gardner 2018-09-03 08:13:13

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.

0
répondu sotn 2018-06-03 12:38:41

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.

-7
répondu Israel Ocbina 2014-10-14 21:37:07