Question Les directives 'using' devraient-elles être à l'intérieur ou à l'extérieur de l'espace de noms?


j'ai couru StyleCop sur un certain code C #, et il continue de signaler que mon using Les directives doivent être à l'intérieur de l'espace de noms.

Y a-t-il une raison technique pour mettre le using directives à l'intérieur au lieu de l'extérieur de l'espace de noms?


1737
2017-09-24 03:49


origine


Réponses:


Il y a en fait une différence (subtile) entre les deux. Imaginez que vous avez le code suivant dans File1.cs:

// File1.cs
using System;
namespace Outer.Inner
{
    class Foo
    {
        static void Bar()
        {
            double d = Math.PI;
        }
    }
}

Maintenant, imaginez que quelqu'un ajoute un autre fichier (File2.cs) au projet qui ressemble à ceci:

// File2.cs
namespace Outer
{
    class Math
    {
    }
}

Les recherches du compilateur Outer avant de regarder les using directives à l'extérieur de l'espace de noms, de sorte qu'il trouve Outer.Math au lieu de System.Math. Malheureusement (ou peut-être heureusement?), Outer.Math n'a pas PI membre, File1 est maintenant cassé.

Cela 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, les recherches du compilateur System avant de chercher Outer, trouve System.Mathet tout va bien.

Certains diraient que Math peut être un mauvais nom pour une classe définie par l'utilisateur, car il y en a déjà un dans System; le point ici est juste là est une différence, et cela affecte la maintenabilité de votre code.

Il est également intéressant de noter ce qui se passe si Foo est dans l'espace de noms Outer, plutôt que Outer.Inner. Dans ce cas, l'ajout Outer.Math dans File2 casse File1 indépendamment de l'endroit où le using va. Cela implique que le compilateur recherche l'espace de noms englobant le plus proche avant de regarder using directif.


1839
2017-09-30 02:33



Ce fil a déjà quelques bonnes réponses, mais je pense que je peux apporter un peu plus de détails avec cette réponse supplémentaire.

D'abord, rappelez-vous qu'une déclaration d'espace de noms avec des points, comme:

namespace MyCorp.TheProduct.SomeModule.Utilities
{
    ...
}

est entièrement équivalent à:

namespace MyCorp
{
    namespace TheProduct
    {
        namespace SomeModule
        {
            namespace Utilities
            {
                ...
            }
        }
    }
}

Si tu le voulais, tu pourrais mettre using directives à tous ces niveaux. (Bien sûr, nous voulons avoir usings dans un seul endroit, mais ce serait légal selon la langue.)

La règle pour résoudre quel type est impliqué, peut être énoncée de manière lâche comme ceci: Cherchez d'abord la "portée" la plus interne pour une correspondance, si rien ne s'y trouve, passez d'un niveau à l'autre portée et recherchez-y, et ainsi de suite, jusqu'à ce qu'une correspondance soit trouvée. Si, à un certain niveau, plus d'une correspondance est trouvée, si l'un des types provient de l'assemblage actuel, choisissez celui-là et lancez un avertissement du compilateur. Sinon, abandonnez (erreur de compilation).

Maintenant, soyons explicites sur ce que cela signifie dans un exemple concret avec les deux conventions majeures.

(1) Avec des utilisations dehors:

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 C (y compris les types imbriqués hérités)
  2. Types dans l'espace de noms actuel MyCorp.TheProduct.SomeModule.Utilities
  3. Types dans l'espace de noms MyCorp.TheProduct.SomeModule
  4. Types dans MyCorp.TheProduct
  5. Types dans MyCorp
  6. Types dans le nul namespace (l'espace de noms global)
  7. Types dans 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, recherchez le type Ambiguous va dans cet ordre:

  1. Types imbriqués à l'intérieur C (y compris les types imbriqués hérités)
  2. Types dans l'espace de noms actuel MyCorp.TheProduct.SomeModule.Utilities
  3. Types dans 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 dans le nul namespace (l'espace de noms global)

(Notez que MyCorp.TheProduct faisait partie de "3." et n'était donc pas nécessaire entre "4." et "5.".)

Remarques de conclusion

Peu importe si vous placez les utilisations à l'intérieur ou à l'extérieur de la déclaration d'espace de noms, il est toujours possible que quelqu'un ajoute un nouveau type avec un nom identique à l'un des espaces de noms ayant une priorité plus élevée.

En outre, si un espace de noms imbriqué a le même nom qu'un type, il peut entraîner des problèmes.

Il est toujours dangereux de déplacer les données d'un emplacement à un autre car la hiérarchie de recherche change et un autre type peut être trouvé. Par conséquent, choisissez une convention et tenez-vous-y, de sorte que vous ne deviez jamais déplacer des utilisations.

Les modèles de Visual Studio, par défaut, mettent les utilisations à l'extérieur de l'espace de noms (par exemple si vous faites que VS génère une nouvelle classe dans un nouveau fichier).

Un (minuscule) avantage d'avoir des utilisations à l'extérieur est que vous pouvez ensuite utiliser les directives using pour un attribut global, par exemple [assembly: ComVisible(false)] au lieu de [assembly: System.Runtime.InteropServices.ComVisible(false)].


345
2018-04-18 21:00



Le placer à l'intérieur des espaces de noms rend les déclarations locales à cet espace de noms pour le fichier (si vous avez plusieurs espaces de noms dans le fichier) mais si vous avez seulement un espace de nom par fichier, cela ne fait pas de différence. à 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;
}

178
2017-09-24 03:52



Selon Hanselman - Utilisation de la directive et de l'assemblage Chargement ... et d'autres articles de ce genre, il n'y a techniquement aucune différence.

Ma préférence est de les mettre hors des espaces de noms.


56
2017-09-24 03:53



Selon la documentation de StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Cause Une directive using C # est placée en dehors d'un élément d'espace de nommage.

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 d'espace de noms, à moins que le fichier ne contienne aucun élément d'espace de nommage.

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. Cependant, on ne sait pas quelle version du type Guid est alloué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 compilateur suivante, trouvée sur la ligne contenant Guid g = new Guid("hello"); 

CS0576: L'espace de noms 'Microsoft.Sample' contient une définition en conflit avec l'alias 'Guid'

Le code crée un alias au type System.Guid appelé Guid et crée également 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 définitions différentes de Guid. Lorsque la directive using-alias est placée en dehors de l'élément namespace, le compilateur choisit la définition locale de Guid définie dans l'espace de noms local et ignore complètement la directive using-alias définie en dehors de l'espace de noms. Ceci, malheureusement, n'est pas évident lors de la lecture du code.

Cependant, lorsque la directive using-alias est positionnée dans l'espace de noms, le compilateur doit choisir entre deux types Guid différents et conflictuels, 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, donc il marque l'erreur du compilateur.

Placer la directive using-alias en dehors de l'espace de noms est une mauvaise pratique car elle peut conduire à la confusion dans des situations comme celle-ci, où il n'est pas évident quelle version du type est réellement utilisée. Cela peut potentiellement conduire à un bug qui pourrait être difficile à diagnostiquer.

Le fait de placer des directives using-alias dans l'élément namespace élimine cela comme une source de bogues.

  1. Espaces de noms multiples

Placer plusieurs éléments d'espace de noms 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 d'espace de noms, plutôt que globalement en haut du fichier. Cela permettra de bien délimiter les espaces de noms et contribuera également à éviter le type de comportement décrit ci-dessus.

Il est important de noter que lorsque du code a été écrit en utilisant des directives placées en dehors de l'espace de noms, il faut prendre soin de déplacer ces directives dans l'espace de noms, pour s'assurer que cela ne change pas la sémantique du code. Comme expliqué ci-dessus, placer des directives using-alias dans l'élément namespace permet au compilateur de choisir entre des types conflictuels d'une manière qui ne se produira pas lorsque les directives seront placées en dehors de l'espace de noms.

Comment réparer les violations Pour corriger une violation de cette règle, déplacez toutes les directives using et using-alias dans l'élément namespace.


45
2017-09-14 15:17



Il y a un problème avec le fait de placer des instructions à l'intérieur de l'espace de noms lorsque vous souhaitez utiliser des alias. L'alias ne bénéficie pas de la précédente using déclarations et doit être pleinement qualifié.

Considérer:

namespace MyNamespace
{
    using System;
    using MyAlias = System.DateTime;

    class MyClass
    {
    }
}

contre:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Cela peut être particulièrement prononcé si vous avez un alias à long manche comme celui-ci (ce qui explique 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 beau.


29
2017-10-10 18:47



Comme Jeppe Stig Nielsen m'a dit, ce fil a déjà de bonnes réponses, mais je pensais que cette subtilité plutôt évidente valait la peine d'être mentionnée.

using Les directives spécifiées à l'intérieur des espaces de noms peuvent rendre le code plus court car ils n'ont pas besoin d'être pleinement qualifiés comme lorsqu'ils sont spécifiés à l'extérieur.

L'exemple suivant fonctionne parce que les types Foo et Bar sont tous deux dans le même espace de noms global, Outer.

Présumer le fichier de code Foo.cs:

namespace Outer.Inner
{
    class Foo { }
}

Et Bar.cs:

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Cela peut omettre l'espace de noms externe dans le using directive, pour faire court:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



Les raisons techniques sont discutées dans les réponses et je pense qu'il s'agit des préférences personnelles à la fin puisque la différence n'est pas que gros et il y a des compromis pour les deux. Le modèle par défaut de Visual Studio pour la création .csfichiers utiliser using les directives en dehors des espaces de noms, par ex.

On peut ajuster stylecop pour vérifier using directives en dehors des espaces de noms en ajoutant stylecop.json fichier à la racine du fichier de projet avec ce qui suit:

{
  "$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 lien existant' pour partager la configuration sur tous vos projets.


0
2018-06-03 12:38



C'est une meilleure pratique si ceux défaut en utilisant i.e. "les références"utilisé dans votre solution source doit être en dehors des espaces de noms et ceux qui sont "nouvelle référence ajoutée" est une bonne pratique est que vous devriez le mettre à l'intérieur de l'espace de noms. C'est pour distinguer quelles références sont ajoutées.


-7
2017-10-14 21:30