c# - visual - vs2017 snippets




Le direttive "using" dovrebbero essere all'interno o all'esterno dello spazio dei nomi? (7)

È una pratica migliore se quelle impostazioni predefinite che utilizzano, ad esempio, i " riferimenti " utilizzati nella soluzione di origine, dovrebbero essere al di fuori degli spazi dei nomi e quelli che sono "nuovi riferimenti aggiunti" è una buona pratica, dovresti inserirli nello spazio dei nomi. Questo per distinguere quali riferimenti vengono aggiunti.

Ho eseguito StyleCop su un codice C # e continua a segnalare che le mie direttive di using dovrebbero essere all'interno dello spazio dei nomi.

Esiste un motivo tecnico per inserire le direttive di using all'interno anziché all'esterno dello spazio dei nomi?


C'è in realtà una (sottile) differenza tra i due. Immagina di avere il seguente codice in File1.cs:

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

Ora immagina che qualcuno aggiunga un altro file (File2.cs) al progetto che assomiglia a questo:

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

Il compilatore cerca in Outer prima di guardare quelli che using direttive al di fuori dello spazio dei nomi, quindi trova Outer.Math invece di System.Math . Sfortunatamente (o forse per fortuna?), Outer.Math non ha alcun membro PI , quindi File1 è ora rotto.

Questo cambia se si inserisce l' using all'interno della dichiarazione dello spazio dei nomi, come segue:

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

Ora il compilatore cerca System prima di cercare Outer , trova System.Math e tutto va bene.

Alcuni sostengono che Math potrebbe essere un brutto nome per una classe definita dall'utente, poiché ce n'è già una in System ; il punto qui è che c'è una differenza e influisce sulla manutenibilità del tuo codice.

È anche interessante notare cosa succede se Foo trova nello spazio dei nomi Outer , piuttosto che Outer.Inner . In tal caso, aggiungendo Outer.Math in File2 si interrompe File1 indipendentemente da dove vada l' using . Ciò implica che il compilatore cerca lo spazio dei nomi che racchiude più interno prima di esaminare qualsiasi direttiva using .


Come ha said Jeppe Stig Nielsen, questo thread ha già ottime risposte, ma ho pensato che anche questa sottigliezza piuttosto ovvia meritasse di essere menzionata.

using direttive specificate all'interno dei namespace può rendere il codice più breve poiché non è necessario essere pienamente qualificati come quando sono specificati all'esterno.

L'esempio seguente funziona perché i tipi Foo e Bar trovano entrambi nello stesso spazio dei nomi globale, Outer .

Presunzione del file di codice Foo.cs :

namespace Outer.Inner
{
    class Foo { }
}

E Bar.cs :

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Questo può omettere lo spazio dei nomi esterno nella direttiva using , in breve:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Inserirlo all'interno degli spazi dei nomi rende le dichiarazioni locali a quello spazio dei nomi per il file (nel caso abbiate più spazi dei nomi nel file) ma se avete solo uno spazio dei nomi per file, allora non fa molta differenza se vanno fuori o all'interno del 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;
}

Questa discussione ha già delle ottime risposte, ma sento di poter dare un po 'più di dettaglio con questa risposta aggiuntiva.

Innanzitutto, ricorda che una dichiarazione dello spazio dei nomi con punti, come:

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

è interamente equivalente a:

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

Se lo volessi, potresti using direttive su tutti questi livelli. (Ovviamente, vogliamo using s in un solo posto, ma sarebbe legale secondo la lingua.)

La regola per la risoluzione di quale tipo è implicito, può essere definita in modo approssimativo come segue: Prima cerca lo "scope" più interno per una corrispondenza, se non viene trovato nulla, esci di un livello nello scope successivo e cerca lì, e così via , fino a quando non viene trovata una corrispondenza. Se a un certo livello viene trovata più di una corrispondenza, se uno dei tipi proviene dall'assieme corrente, sceglierne uno ed emettere un avviso del compilatore. Altrimenti, arrendersi (errore in fase di compilazione).

Ora, diciamo esplicitamente cosa significa questo in un esempio concreto con le due principali convenzioni.

(1) Con gli usi esterni:

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

Nel caso precedente, per scoprire che tipo è Ambiguous , la ricerca va in questo ordine:

  1. Tipi annidati all'interno di C (inclusi tipi annidati ereditati)
  2. Tipi nello spazio dei nomi corrente MyCorp.TheProduct.SomeModule.Utilities
  3. Tipi nello spazio MyCorp.TheProduct.SomeModule nomi MyCorp.TheProduct.SomeModule
  4. Tipi in MyCorp.TheProduct
  5. Tipi in MyCorp
  6. Tipi nello spazio dei nomi null (lo spazio dei nomi globale)
  7. Tipi in System , System.Collections.Generic , System.Linq , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration e ThirdParty

L'altra convenzione:

(2) Con le usanze all'interno:

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

Ora, cerca il tipo Ambiguous in questo ordine:

  1. Tipi annidati all'interno di C (inclusi tipi annidati ereditati)
  2. Tipi nello spazio dei nomi corrente MyCorp.TheProduct.SomeModule.Utilities
  3. Tipi in System , System.Collections.Generic , System.Linq , MyCorp.TheProduct , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration e ThirdParty
  4. Tipi nello spazio MyCorp.TheProduct.SomeModule nomi MyCorp.TheProduct.SomeModule
  5. Tipi in MyCorp
  6. Tipi nello spazio dei nomi null (lo spazio dei nomi globale)

(Si noti che MyCorp.TheProduct faceva parte di "3." e non era quindi necessario tra "4." e "5.".)

Osservazioni conclusive

Non importa se si inseriscono gli usi all'interno o all'esterno della dichiarazione dello spazio dei nomi, c'è sempre la possibilità che qualcuno aggiunga successivamente un nuovo tipo con un nome identico a uno degli spazi dei nomi che hanno una priorità più alta.

Inoltre, se uno spazio dei nomi annidato ha lo stesso nome di un tipo, può causare problemi.

È sempre pericoloso spostare gli usi da una posizione a un'altra perché la gerarchia della ricerca cambia e un altro tipo può essere trovato. Pertanto, scegli una convenzione e atteniti ad essa, in modo da non dover mai spostare gli usi.

I modelli di Visual Studio, per impostazione predefinita, inseriscono gli utilizzi al di fuori dello spazio dei nomi (ad esempio se si crea VS in una nuova classe in un nuovo file).

Un (piccolo) vantaggio di avere usi esterni è che è quindi possibile utilizzare le direttive using per un attributo globale, ad esempio [assembly: ComVisible(false)] invece di [assembly: System.Runtime.InteropServices.ComVisible(false)] .



Un'altra sottigliezza che non credo sia stata coperta dalle altre risposte è per quando hai una classe e un namespace con lo stesso nome.

Quando hai l'importazione all'interno dello spazio dei nomi, troverà la classe. Se l'importazione è al di fuori dello spazio dei nomi, l'importazione verrà ignorata e la classe e lo spazio dei nomi devono essere pienamente qualificati.

//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.
        }
    }
}






code-organization