Question

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

Existe-t-il une raison technique pour que le code utilisant les directives soit inséré à l'extérieur de l'espace de noms?

Était-ce utile?

La solution

Il existe 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;
        }
    }
}

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

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

Le compilateur recherche extérieur avant d'examiner ces à l'aide de directives en dehors de l'espace de noms, afin de trouver Outer.Math au lieu de Système .Math . Malheureusement (ou peut-être heureusement?), Outer.Math n'a pas de membre PI , donc File1 est maintenant cassé.

Cela change si vous mettez le à l'aide de 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 Système avant de rechercher Extérieur , trouve System.Math et tout va bien.

Certains diront que Math pourrait être un mauvais nom pour une classe définie par l'utilisateur, car il en existe déjà un dans System ; l’important, c’est que il y a une différence qui 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 de Outer.Math dans File2 rompt File1 indépendamment de l'emplacement où utilisant va. Cela implique que le compilateur recherche l’espace de noms le plus intérieur englobant avant d’examiner toute directive utilisant la directive .

Autres conseils

Ce fil contient déjà d'excellentes réponses, mais j'estime pouvoir 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 points, telle que:

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

est entièrement équivalent à:

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

Si vous le souhaitez, vous pouvez mettre en utilisant des directives à tous ces niveaux. (Bien sûr, nous voulons que utilisant s dans un seul endroit, mais ce serait légal selon la langue.)

La règle permettant de déterminer le type impliqué, peut être formulée de la manière suivante: Recherchez tout d'abord le "scope" le plus à l'intérieur pour une correspondance, si rien n’est trouvé, passez d’un niveau à l’autre et effectuez une recherche à cet endroit, 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'assembly actuel, sélectionnez-le et émettez un avertissement du compilateur. Sinon, abandonnez (erreur de compilation).

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

(1) Avec les 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 rechercher le type Ambigu , la recherche s'effectue dans cet ordre:

  1. Types imbriqués dans 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. Taper dans MyCorp.TheProduct
  5. Taper dans MyCorp
  6. Types dans l'espace de noms null (espace de noms global)
  7. Types dans Système , System.Collections.Generic , System.Linq , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration et ThirdParty

L'autre convention:

(2) Avec les 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 Ambigu va dans cet ordre:

  1. Types imbriqués dans 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 Système , System.Collections.Generic , System.Linq , MyCorp.TheProduct , < code> MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration et Troisième Partie
  4. Types dans l'espace de noms MyCorp.TheProduct.SomeModule
  5. Taper dans MyCorp
  6. Types dans l'espace de noms null (espace de noms global)

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

Remarques finales

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

De même, si un espace de nom imbriqué a le même nom qu'un type, il peut en résulter des problèmes.

Il est toujours dangereux de déplacer les utilisations 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 respectez-la afin de ne pas avoir à déplacer les utilisateurs.

Les modèles de Visual Studio placent par défaut les utilisations en dehors de l'espace de nom (par exemple, si vous obligez VS à générer une nouvelle classe dans un nouveau fichier).

Un (petit) avantage d'avoir des utilisations hors de 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)] .

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 n'avez qu'un seul espace de noms par fichier, le fait qu'ils aller à 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 - Utilisation du chargement de directives et d'assemblages ... et d'autres articles du même genre il n'y a techniquement aucune différence.

Ma préférence est de les placer en dehors des espaces de noms.

Selon la documentation StyleCop:

SA1200: Utilisation de DirectivesMustBePlacedWithinNamespace

Cause Une directive utilisant C # est placée à l'extérieur d'un élément d'espace de noms.

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

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
    {
    }
}

Cependant, le code suivant n'entraînera aucune violation de cette règle:

namespace Microsoft.Sample
{
    using System;
    using Guid = System.Guid;

    public class Program
    {
    }
}

Ce code se compilera proprement, sans erreur de compilation. Cependant, la version du type Guid allouée n’est pas clairement définie. Si la directive using est déplacée à l'intérieur de l'espace de noms, comme indiqué ci-dessous, une erreur du compilateur 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 a échoué avec l'erreur de compilation 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 pour le 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 à l'extérieur 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 à l'extérieur de l'espace de noms. Malheureusement, cela n’est pas évident lors de la lecture du code.

Lorsque la directive using-alias est positionnée dans l'espace de noms, le compilateur doit choisir entre deux types de 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 étant incapable de prendre une décision, il signale son erreur.

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

Le fait de placer des directives using-alias dans l’élément namespace l’élimine en tant que source de bogues.

  1. Plusieurs espaces de noms

Placer plusieurs éléments d’espace de noms dans un même fichier est généralement une mauvaise idée, mais si cela est fait, il est judicieux de placer toutes les directives d’utilisation dans chacun des éléments d’espace de noms, plutôt que de les placer globalement en haut de la liste. fichier. Cela couvrira étroitement les espaces de noms et aidera également à éviter le type de comportement décrit ci-dessus.

Il est important de noter que, lorsque le code a été écrit avec des directives placées en dehors de l’espace de noms, vous devez faire attention lorsque vous les déplacez dans l’espace de noms afin de ne pas modifier la sémantique du code. Comme expliqué ci-dessus, le fait de placer des directives using-alias dans l'élément namespace permet au compilateur de choisir entre des types en conflit d'une manière qui ne se produira pas si les directives sont 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 les directives using-alias dans l'élément namespace.

Il est problématique de placer des instructions using dans l’espace de noms lorsque vous souhaitez utiliser des alias. L'alias ne bénéficie pas de la précédente en utilisant et doit être qualifié complet.

Considérez:

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

    class MyClass
    {
    }
}

versus:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Cela peut être particulièrement prononcé si vous avez un alias à longue haleine tel que le suivant (voici comment j'ai trouvé le problème):

using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;

Si utilise des instructions dans l'espace de noms, cela devient tout à coup:

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 dit Jeppe Stig Nielsen , ce fil contient déjà d'excellentes réponses, mais je pensais qu'il valait la peine de le mentionner. aussi.

en utilisant les directives spécifiées à l'intérieur des espaces de noms peuvent permettre de raccourcir le code car elles n'ont pas besoin d'être qualifiées comme elles sont spécifiées à l'extérieur.

L'exemple suivant fonctionne parce que les types Foo et Bar sont tous deux dans le même espace de nom 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 la directive à l'aide de la directive , en abrégé:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Une ride que j'ai rencontrée (qui n'est pas couverte dans les autres réponses):

Supposons que vous ayez ces espaces de noms:

  • quelque chose.autres
  • Parent.Quelque chose.Autre

Lorsque vous utilisez en utilisant quelque chose.Other en dehors de d'un espace de nom Parent , il fait référence au premier (Something.Other).

Cependant, si vous l'utilisez dans de cette déclaration d'espace de noms, il fait référence à la seconde (Parent.Something.Other)!

Il existe une solution simple: ajoutez le " global :: " préfixe: docs

namespace Parent
{
   using global::Something.Other;
   // etc
}

Une autre subtilité qui, à mon avis, n'a pas été abordée dans les autres réponses concerne les situations où vous avez une classe et un espace de noms du même nom.

Lorsque vous avez importé à l'intérieur de l'espace de noms, il trouvera la classe. Si l'importation se trouve en dehors de l'espace de noms, l'importation sera ignorée et la classe et l'espace de noms doivent être entièrement 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 s’agit des préférences personnelles à la fin, car la différence n’est pas que Big et il existe des compromis pour les deux. Le modèle par défaut de Visual Studio pour la création de fichiers .cs utilise en utilisant des directives en dehors des espaces de noms, par exemple.

On peut ajuster stylecop pour vérifier en utilisant des directives en dehors des espaces de noms en ajoutant un fichier stylecop.json à la racine du fichier de projet avec les éléments suivants:

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

Il est préférable que les utilisateurs par défaut utilisant " références" " Les sources utilisées dans votre solution source doivent se trouver en dehors des espaces de noms. Les "nouvelles références ajoutées" sont une bonne pratique. Vous devez les insérer dans l'espace de noms. Ceci permet de distinguer les références ajoutées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top