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





code resharper (8)


Selon la documentation de StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Cause AC # using directive est placé en dehors d'un élément d'espace de nom.

Règle Description 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 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 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 corriger les violations Pour corriger une violation de cette règle, déplacez toutes les directives using et using-alias dans l'élément namespace.

J'ai exécuté StyleCop sur du code C #, et il continue de signaler que mes directives using devraient être dans l'espace de noms.

Y a-t-il une raison technique pour mettre les directives using intérieur et non à l'extérieur de l'espace de noms?




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 des instructions d' using antérieures 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 terme tel que celui-ci (c'est ainsi que j'ai trouvé le problème):

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

En using instructions à l'intérieur de l'espace de noms, il devient soudain:

using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;

Pas beau.




Comme Jeppe Stig Nielsen l'a said , 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 directives spécifiées à l'intérieur des espaces de nommage peut rendre le code plus court, car il n'est pas nécessaire de les qualifier complètement comme 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;
    }
}

Cela peut omettre l'espace de nom externe dans la directive using , en bref:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}



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

Le compilateur effectue une recherche sur Outer avant de regarder ceux qui using directives 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 aucun membre PI , donc File1 est maintenant cassé.

Cela change si vous mettez l' 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 rechercher Outer , trouve System.Math , et tout va bien.

Certains diront que Math peut être un mauvais nom pour une classe définie par l'utilisateur, puisqu'il y en a déjà une dans System ; le point ici est juste qu'il y a 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 dans Outer.Inner . Dans ce cas, l'ajout de Outer.Math dans File2 casse File1 indépendamment de l'emplacement de l' using . Cela implique que le compilateur recherche l'espace de noms englobant le plus proche avant de regarder toute directive using .







C'est une meilleure pratique si les " références " utilisées par défaut dans votre solution source doivent être en dehors des espaces de noms et celles qui sont "nouvelle référence ajoutée" est une bonne pratique à mettre dans l'espace de noms. C'est pour distinguer quelles références sont ajoutées.




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 vous le souhaitez, vous pouvez using directives à tous ces niveaux. (Bien sûr, nous voulons using s dans un seul endroit, mais ce serait légal selon la langue.)

La règle pour résoudre quel type est implicite, 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'étendue suivante et y cherchez, 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 est Ambiguous , la recherche s'effectue 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 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 l'espace de noms nul (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 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 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 l'espace de noms nul (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.

Par défaut, les modèles de Visual Studio placent les utilisations en dehors de l'espace de noms (par exemple, si VS fait générer 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)] .




Ayant été mordu par cela, j'ai l'habitude d'inclure des variables définies localement dans la portée la plus interne que j'utilise pour transférer à n'importe quelle fermeture. Dans votre exemple:

foreach (var s in strings)
{
    query = query.Where(i => i.Prop == s); // access to modified closure

Je fais:

foreach (var s in strings)
{
    string search = s;
    query = query.Where(i => i.Prop == search); // New definition ensures unique per iteration.

Une fois que vous avez cette habitude, vous pouvez l'éviter dans le cas très rare que vous avez réellement l'intention de lier aux portées externes. Pour être honnête, je ne pense pas l'avoir jamais fait.





c# .net namespaces stylecop code-organization