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


ho corso StyleCop su qualche codice C #, e continua a riportare che il mio using le direttive dovrebbero essere all'interno dello spazio dei nomi.

C'è una ragione tecnica per mettere il using direttive all'interno anziché all'esterno dello spazio dei nomi?


1736
2017-09-24 03:49


origine


risposte:


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 Outer prima di guardarli 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 PI membro, quindi File1 è ora rotto.

Questo cambia se metti il 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.Mathe tutto va bene.

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

È anche interessante notare cosa succede se Foo è nel namespace Outer, piuttosto che Outer.Inner. In tal caso, aggiungendo Outer.Math in File2 interrompe File1 indipendentemente da dove si trova il file using va. Ciò implica che il compilatore cerca lo spazio dei nomi più interno prima di guardarlo using direttiva.


1839
2017-09-30 02:33



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 metterlo using direttive su tutti questi livelli. (Certo, vogliamo averlo usings in un solo posto, ma sarebbe legale secondo la lingua.)

La regola per la risoluzione di quale tipo è implicito, può essere definita come segue: Per prima cosa cerca lo "scope" più interno per una corrispondenza, se non viene trovato nulla, esci di un livello allo 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 di che tipo Ambiguous è, la ricerca va in questo ordine:

  1. Tipi annidati all'interno C (compresi i tipi annidati ereditati)
  2. Tipi nello spazio dei nomi corrente MyCorp.TheProduct.SomeModule.Utilities
  3. Tipi nello spazio dei nomi MyCorp.TheProduct.SomeModule
  4. Tipi in MyCorp.TheProduct
  5. Tipi in MyCorp
  6. Tipi nel nullo namespace (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 va in questo ordine:

  1. Tipi annidati all'interno C (compresi i 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 dei nomi MyCorp.TheProduct.SomeModule
  5. Tipi in MyCorp
  6. Tipi nel nullo namespace (lo spazio dei nomi globale)

(Nota che MyCorp.TheProduct era una parte di "3." e quindi non era 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 usi al di fuori dello spazio dei nomi (ad esempio se si crea VS una nuova classe in un nuovo file).

Uno (piccolo) vantaggio di avere degli usi al di fuori è che puoi quindi utilizzare le direttive using per un attributo globale, per esempio [assembly: ComVisible(false)] invece di [assembly: System.Runtime.InteropServices.ComVisible(false)].


345
2018-04-18 21:00



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

178
2017-09-24 03:52



Secondo Hanselman - Uso della direttiva e dell'assemblaggio Caricamento in corso ... e altri articoli simili non c'è tecnicamente alcuna differenza.

La mia preferenza è metterli fuori dagli spazi dei nomi.


56
2017-09-24 03:53



Secondo la documentazione di StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Causa Una direttiva C # viene posizionata all'esterno di un elemento dello spazio dei nomi.

Descrizione della regola Una violazione di questa regola si verifica quando una direttiva using o una direttiva using-alias viene posta all'esterno di un elemento dello spazio dei nomi, a meno che il file non contenga alcun elemento dello spazio dei nomi.

Ad esempio, il codice seguente comporterebbe due violazioni di questa regola.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

Il seguente codice, tuttavia, non comporterebbe alcuna violazione di questa regola:

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

    public class Program
    {
    }
}

Questo codice verrà compilato in modo pulito, senza errori del compilatore. Tuttavia, non è chiaro quale versione del tipo Guid sia stata assegnata. Se la direttiva using viene spostata all'interno dello spazio dei nomi, come mostrato di seguito, si verificherà un errore del compilatore:

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

Il codice non riesce nel seguente errore del compilatore, trovato nella riga che contiene Guid g = new Guid("hello"); 

CS0576: Namespace 'Microsoft.Sample' contiene una definizione in conflitto con l'alias 'Guid'

Il codice crea un alias per il tipo System.Guid chiamato Guid e crea anche il proprio tipo chiamato Guid con un'interfaccia di costruzione corrispondente. Successivamente, il codice crea un'istanza del tipo Guid. Per creare questa istanza, il compilatore deve scegliere tra le due diverse definizioni di Guid. Quando la direttiva using-alias viene posta all'esterno dell'elemento namespace, il compilatore sceglierà la definizione locale di Guid definita all'interno dello spazio dei nomi locale e ignorerà completamente la direttiva using-alias definita al di fuori dello spazio dei nomi. Questo, sfortunatamente, non è ovvio quando si legge il codice.

Quando la direttiva using alias viene posizionata all'interno dello spazio dei nomi, tuttavia, il compilatore deve scegliere tra due diversi tipi di Guida in conflitto, entrambi definiti all'interno dello stesso spazio dei nomi. Entrambi questi tipi forniscono un costruttore corrispondente. Il compilatore non è in grado di prendere una decisione, quindi segnala l'errore del compilatore.

Posizionare la direttiva using-alias al di fuori dello spazio dei nomi è una cattiva pratica perché può portare a confusione in situazioni come questa, dove non è ovvio quale versione del tipo sia effettivamente utilizzata. Questo può potenzialmente portare a un bug che potrebbe essere difficile da diagnosticare.

Inserendo le direttive using-alias all'interno dell'elemento namespace si elimina questa come una fonte di bug.

  1. Namespace multipli

Posizionare più elementi dello spazio dei nomi all'interno di un singolo file è generalmente una cattiva idea, ma se e quando viene fatto, è una buona idea posizionare tutti usando le direttive all'interno di ciascuno degli elementi dello spazio dei nomi, piuttosto che globalmente nella parte superiore del file. Ciò ridurrà strettamente lo spazio dei nomi e aiuterà anche a evitare il tipo di comportamento descritto sopra.

È importante notare che quando il codice è stato scritto con l'uso di direttive poste al di fuori dello spazio dei nomi, è necessario prestare attenzione quando si spostano queste direttive all'interno dello spazio dei nomi, per garantire che ciò non stia cambiando la semantica del codice. Come spiegato sopra, posizionare le direttive using-alias all'interno dell'elemento namespace consente al compilatore di scegliere tra i tipi in conflitto in modi che non si verifichino quando le direttive vengono posizionate al di fuori dello spazio dei nomi.

Come correggere le violazioni Per correggere una violazione di questa regola, spostare tutto usando le direttive e le direttive using alias all'interno dell'elemento namespace.


45
2017-09-14 15:17



C'è un problema con l'inserimento di istruzioni all'interno dello spazio dei nomi quando si desidera utilizzare alias. L'alias non beneficia del precedente using dichiarazioni e deve essere pienamente qualificato.

Prendere in considerazione:

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

    class MyClass
    {
    }
}

contro:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Questo può essere particolarmente pronunciato se si ha un alias prolisso come il seguente (che è il modo in cui ho trovato il problema):

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

Con using dichiarazioni all'interno del namespace, diventa improvvisamente:

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

Non carino.


29
2017-10-10 18:47



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

using le direttive specificate all'interno dei namespace possono creare codice più breve poiché non è necessario che siano pienamente qualificate come quando sono specificate all'esterno.

L'esempio seguente funziona perché i tipi Foo e Bar sono 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 nel using direttiva, in breve:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



Le ragioni tecniche sono discusse nelle risposte e penso che alla fine le preferenze personali siano le preferenze personali, poiché la differenza non è quella grande e ci sono dei compromessi per entrambi. Modello predefinito di Visual Studio per la creazione .csutilizzo dei file using direttive al di fuori di namespace, ad es.

Si può regolare stylecop per controllare using direttive al di fuori dei namespace attraverso l'aggiunta stylecop.json file nella radice del file di progetto con il seguente:

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

Puoi creare questo file di configurazione a livello di soluzione e aggiungerlo ai tuoi progetti come "File di collegamento esistente" per condividere la configurazione anche su tutti i tuoi progetti.


0
2018-06-03 12:38



È una pratica migliore se quelle predefinito usando i.e. "Riferimenti"usato nella tua soluzione di origine dovrebbe essere al di fuori degli spazi dei nomi e di quelli che sono "nuovo riferimento aggiunto" è una buona pratica dovresti metterlo nello spazio dei nomi. Questo per distinguere quali riferimenti vengono aggiunti.


-7
2017-10-14 21:30