Should 'using' directives be inside or outside the namespace?

前端 未结 12 1393
长情又很酷
长情又很酷 2020-11-21 22:41

I have been running StyleCop over some C# code, and it keeps reporting that my using directives should be inside the namespace.

Is there a technical rea

相关标签:
12条回答
  • 2020-11-21 23:03

    As Jeppe Stig Nielsen said, this thread already has great answers, but I thought this rather obvious subtlety was worth mentioning too.

    using directives specified inside namespaces can make for shorter code since they don't need to be fully qualified as when they're specified on the outside.

    The following example works because the types Foo and Bar are both in the same global namespace, Outer.

    Presume the code file Foo.cs:

    namespace Outer.Inner
    {
        class Foo { }
    }
    

    And Bar.cs:

    namespace Outer
    {
        using Outer.Inner;
    
        class Bar
        {
            public Foo foo;
        }
    }
    

    That may omit the outer namespace in the using directive, for short:

    namespace Outer
    {
        using Inner;
    
        class Bar
        {
            public Foo foo;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 23:05

    The technical reasons are discussed in the answers and I think that it comes to the personal preferences in the end since the difference is not that big and there are tradeoffs for both of them. Visual Studio's default template for creating .cs files use using directives outside of namespaces e.g.

    One can adjust stylecop to check using directives outside of namespaces through adding stylecop.json file in the root of the project file with the following:

    {
      "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
        "orderingRules": {
          "usingDirectivesPlacement": "outsideNamespace"
        }
      }
    }
    

    You can create this config file in solution level and add it to your projects as 'Existing Link File' to share the config across all of your projects too.

    0 讨论(0)
  • 2020-11-21 23:08

    According to Hanselman - Using Directive and Assembly Loading... and other such articles there is technically no difference.

    My preference is to put them outside of namespaces.

    0 讨论(0)
  • 2020-11-21 23:10

    When citing Microsoft's internal guidelines, keep in mind that they're written by somebody who probably has less than ten years experience coding. In other words, they're likely based on nothing more solid than personal preference. Especially in something like C# that's so new.

    As a rule, external using directives (System and Microsoft namespaces for example) should be placed outside the namespace directive. They are defaults that should be applied in all cases unless otherwise specified. This should include any of your own organization's internal libraries that are not part of the current project, or using directives that reference other primary namespaces in the same project. Any using directives that reference other modules in the current project and namespace should be placed inside the namespace directive. This serves two specific functions:

    • It provides a visual distinction between local modules and 'other' modules, meaning everything else.
    • It scopes the local directives to be applied preferentially over global directives.

    The latter reason is significant. It means that it's harder to introduce an ambiguous reference issue that can be introduced by a change no more significant than refactoring code. That is to say, you move a method from one file to another and suddenly a bug shows up that wasn't there before. Colloquially, a 'heisenbug' - historically fiendishly difficult to track down.

    As an even more general rule, a good one to follow is this. If you see something intrinsic to a language that seems to be a useless option, assume that it's NOT. In fact, the harder it is to see why the option exists, the more important you should assume it is. Do the research about the specific differences between the two options and then think long and hard about the implications. You'll usually find an amazingly insightful and clever solution to an obscure problem that the language designer put in there specifically to make your life easier. Be appropriately grateful and take advantage of it.

    0 讨论(0)
  • 2020-11-21 23:12

    There is actually a (subtle) difference between the two. Imagine you have the following code in File1.cs:

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

    Now imagine that someone adds another file (File2.cs) to the project that looks like this:

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

    The compiler searches Outer before looking at those using directives outside the namespace, so it finds Outer.Math instead of System.Math. Unfortunately (or perhaps fortunately?), Outer.Math has no PI member, so File1 is now broken.

    This changes if you put the using inside your namespace declaration, as follows:

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

    Now the compiler searches System before searching Outer, finds System.Math, and all is well.

    Some would argue that Math might be a bad name for a user-defined class, since there's already one in System; the point here is just that there is a difference, and it affects the maintainability of your code.

    It's also interesting to note what happens if Foo is in namespace Outer, rather than Outer.Inner. In that case, adding Outer.Math in File2 breaks File1 regardless of where the using goes. This implies that the compiler searches the innermost enclosing namespace before it looks at any using directive.

    0 讨论(0)
  • 2020-11-21 23:12

    According the to StyleCop Documentation:

    SA1200: UsingDirectivesMustBePlacedWithinNamespace

    Cause A C# using directive is placed outside of a namespace element.

    Rule Description A violation of this rule occurs when a using directive or a using-alias directive is placed outside of a namespace element, unless the file does not contain any namespace elements.

    For example, the following code would result in two violations of this rule.

    using System;
    using Guid = System.Guid;
    
    namespace Microsoft.Sample
    {
        public class Program
        {
        }
    }
    

    The following code, however, would not result in any violations of this rule:

    namespace Microsoft.Sample
    {
        using System;
        using Guid = System.Guid;
    
        public class Program
        {
        }
    }
    

    This code will compile cleanly, without any compiler errors. However, it is unclear which version of the Guid type is being allocated. If the using directive is moved inside of the namespace, as shown below, a compiler error will occur:

    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");
            }
        }
    }
    

    The code fails on the following compiler error, found on the line containing Guid g = new Guid("hello");

    CS0576: Namespace 'Microsoft.Sample' contains a definition conflicting with alias 'Guid'

    The code creates an alias to the System.Guid type called Guid, and also creates its own type called Guid with a matching constructor interface. Later, the code creates an instance of the type Guid. To create this instance, the compiler must choose between the two different definitions of Guid. When the using-alias directive is placed outside of the namespace element, the compiler will choose the local definition of Guid defined within the local namespace, and completely ignore the using-alias directive defined outside of the namespace. This, unfortunately, is not obvious when reading the code.

    When the using-alias directive is positioned within the namespace, however, the compiler has to choose between two different, conflicting Guid types both defined within the same namespace. Both of these types provide a matching constructor. The compiler is unable to make a decision, so it flags the compiler error.

    Placing the using-alias directive outside of the namespace is a bad practice because it can lead to confusion in situations such as this, where it is not obvious which version of the type is actually being used. This can potentially lead to a bug which might be difficult to diagnose.

    Placing using-alias directives within the namespace element eliminates this as a source of bugs.

    1. Multiple Namespaces

    Placing multiple namespace elements within a single file is generally a bad idea, but if and when this is done, it is a good idea to place all using directives within each of the namespace elements, rather than globally at the top of the file. This will scope the namespaces tightly, and will also help to avoid the kind of behavior described above.

    It is important to note that when code has been written with using directives placed outside of the namespace, care should be taken when moving these directives within the namespace, to ensure that this is not changing the semantics of the code. As explained above, placing using-alias directives within the namespace element allows the compiler to choose between conflicting types in ways that will not happen when the directives are placed outside of the namespace.

    How to Fix Violations To fix a violation of this rule, move all using directives and using-alias directives within the namespace element.

    0 讨论(0)
提交回复
热议问题