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

前端 未结 12 1391
长情又很酷
长情又很酷 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 22:49

    One wrinkle I ran into (that isn't covered in other answers):

    Suppose you have these namespaces:

    • Something.Other
    • Parent.Something.Other

    When you use using Something.Other outside of a namespace Parent, it refers to the first one (Something.Other).

    However if you use it inside of that namespace declaration, it refers to the second one (Parent.Something.Other)!

    There is a simple solution: add the "global::" prefix: docs

    namespace Parent
    {
       using global::Something.Other;
       // etc
    }
    
    0 讨论(0)
  • 2020-11-21 22:51

    This thread already has some great answers, but I feel I can bring a little more detail with this additional answer.

    First, remember that a namespace declaration with periods, like:

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

    is entirely equivalent to:

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

    If you wanted to, you could put using directives on all of these levels. (Of course, we want to have usings in only one place, but it would be legal according to the language.)

    The rule for resolving which type is implied, can be loosely stated like this: First search the inner-most "scope" for a match, if nothing is found there go out one level to the next scope and search there, and so on, until a match is found. If at some level more than one match is found, if one of the types are from the current assembly, pick that one and issue a compiler warning. Otherwise, give up (compile-time error).

    Now, let's be explicit about what this means in a concrete example with the two major conventions.

    (1) With usings outside:

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

    In the above case, to find out what type Ambiguous is, the search goes in this order:

    1. Nested types inside C (including inherited nested types)
    2. Types in the current namespace MyCorp.TheProduct.SomeModule.Utilities
    3. Types in namespace MyCorp.TheProduct.SomeModule
    4. Types in MyCorp.TheProduct
    5. Types in MyCorp
    6. Types in the null namespace (the global namespace)
    7. Types in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, and ThirdParty

    The other convention:

    (2) With usings inside:

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

    Now, search for the type Ambiguous goes in this order:

    1. Nested types inside C (including inherited nested types)
    2. Types in the current namespace MyCorp.TheProduct.SomeModule.Utilities
    3. Types in System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, and ThirdParty
    4. Types in namespace MyCorp.TheProduct.SomeModule
    5. Types in MyCorp
    6. Types in the null namespace (the global namespace)

    (Note that MyCorp.TheProduct was a part of "3." and was therefore not needed between "4." and "5.".)

    Concluding remarks

    No matter if you put the usings inside or outside the namespace declaration, there's always the possibility that someone later adds a new type with identical name to one of the namespaces which have higher priority.

    Also, if a nested namespace has the same name as a type, it can cause problems.

    It is always dangerous to move the usings from one location to another because the search hierarchy changes, and another type may be found. Therefore, choose one convention and stick to it, so that you won't have to ever move usings.

    Visual Studio's templates, by default, put the usings outside of the namespace (for example if you make VS generate a new class in a new file).

    One (tiny) advantage of having usings outside is that you can then utilize the using directives for a global attribute, for example [assembly: ComVisible(false)] instead of [assembly: System.Runtime.InteropServices.ComVisible(false)].

    0 讨论(0)
  • 2020-11-21 22:56

    There is an issue with placing using statements inside the namespace when you wish to use aliases. The alias doesn't benefit from the earlier using statements and has to be fully qualified.

    Consider:

    namespace MyNamespace
    {
        using System;
        using MyAlias = System.DateTime;
    
        class MyClass
        {
        }
    }
    

    versus:

    using System;
    
    namespace MyNamespace
    {
        using MyAlias = DateTime;
    
        class MyClass
        {
        }
    }
    

    This can be particularly pronounced if you have a long-winded alias such as the following (which is how I found the problem):

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

    With using statements inside the namespace, it suddenly becomes:

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

    Not pretty.

    0 讨论(0)
  • 2020-11-21 22:58

    Another subtlety that I don't believe has been covered by the other answers is for when you have a class and namespace with the same name.

    When you have the import inside the namespace then it will find the class. If the import is outside the namespace then the import will be ignored and the class and namespace have to be fully qualified.

    //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.
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 22:58

    It is a better practice if those default using i.e. "references" used in your source solution should be outside the namespaces and those that are "new added reference" is a good practice is you should put it inside the namespace. This is to distinguish what references are being added.

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

    Putting it inside the namespaces makes the declarations local to that namespace for the file (in case you have multiple namespaces in the file) but if you only have one namespace per file then it doesn't make much of a difference whether they go outside or inside the namespace.

    using ThisNamespace.IsImported.InAllNamespaces.Here;
    
    namespace Namespace1
    { 
       using ThisNamespace.IsImported.InNamespace1.AndNamespace2;
    
       namespace Namespace2
       { 
          using ThisNamespace.IsImported.InJustNamespace2;
       }       
    }
    
    namespace Namespace3
    { 
       using ThisNamespace.IsImported.InJustNamespace3;
    }
    
    0 讨论(0)
提交回复
热议问题