¿Deberían las directivas ‘using’ estar dentro o fuera del espacio de nombres?

He estado ejecutando StyleCop sobre algún código de C #, y sigue informando que mis directivas de using deben estar dentro del espacio de nombres.

¿Existe alguna razón técnica para colocar las directivas de using dentro del espacio de nombres?

En realidad, hay una diferencia (sutil) entre los dos. Imagine que tiene el siguiente código en File1.cs:

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

Ahora imagine que alguien agrega otro archivo (File2.cs) al proyecto que se ve así:

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

El comstackdor busca en el Outer antes de mirar aquellos que using directivas fuera del espacio de nombres, por lo que encuentra Outer.Math lugar de System.Math . Desafortunadamente (¿o quizás afortunadamente?), Outer.Math no tiene un miembro de PI , por lo que File1 ahora está roto.

Esto cambia si coloca el using dentro de su statement de espacio de nombres, de la siguiente manera:

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

Ahora el comstackdor busca System antes de buscar Outer , encuentra System.Math , y todo está bien.

Algunos argumentarían que Math podría ser un mal nombre para una clase definida por el usuario, ya que ya existe una en System ; el punto aquí es que hay una diferencia, y afecta la mantenibilidad de su código.

También es interesante observar lo que sucede si Foo está en el espacio de nombres Outer , en lugar de Outer.Inner . Outer.Inner . En ese caso, agregar Outer.Math en File2 rompe File1 independientemente de dónde va el using . Esto implica que el comstackdor busca en el espacio de nombre adjunto más interno antes de mirar cualquier directiva de using .

Este hilo ya tiene algunas respuestas excelentes, pero creo que puedo aportar un poco más de detalle con esta respuesta adicional.

Primero, recuerda que una statement de espacio de nombres con puntos, como:

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

es completamente equivalente a:

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

Si quisieras, podrías using el using directivas en todos estos niveles. (Por supuesto, queremos tener s en un solo lugar, pero sería legal según el idioma).

La regla para resolver qué tipo está implícito, se puede express de forma general así: Primero busque el “scope” más interno para una coincidencia, si no se encuentra nada, salga de un nivel al siguiente y busque allí, y así sucesivamente , hasta que se encuentre una coincidencia. Si en algún nivel se encuentra más de una coincidencia, si uno de los tipos proviene del ensamblado actual, selecciónelo y emita una advertencia del comstackdor. De lo contrario, renunciar (error en tiempo de comstackción).

Ahora, seamos explícitos sobre lo que esto significa en un ejemplo concreto con las dos convenciones principales.

(1) Con usos fuera:

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

En el caso anterior, para averiguar qué tipo es Ambiguous , la búsqueda va en este orden:

  1. Tipos nesteds dentro de C (incluidos los tipos nesteds heredados)
  2. Tipos en el espacio de nombre actual MyCorp.TheProduct.SomeModule.Utilities
  3. Tipos en el espacio de nombres MyCorp.TheProduct.SomeModule
  4. Tipos en MyCorp.TheProduct
  5. Tipos en MyCorp
  6. Tipos en el espacio de nombres nulo (el espacio de nombres global)
  7. Tipos en System , System.Collections.Generic , System.Linq , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration y ThirdParty

La otra convención:

(2) Con usos dentro:

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

Ahora, busque el tipo Ambiguous en este orden:

  1. Tipos nesteds dentro de C (incluidos los tipos nesteds heredados)
  2. Tipos en el espacio de nombre actual MyCorp.TheProduct.SomeModule.Utilities
  3. Tipos en System , System.Collections.Generic , System.Linq , MyCorp.TheProduct , MyCorp.TheProduct.OtherModule , MyCorp.TheProduct.OtherModule.Integration y ThirdParty
  4. Tipos en el espacio de nombres MyCorp.TheProduct.SomeModule
  5. Tipos en MyCorp
  6. Tipos en el espacio de nombres nulo (el espacio de nombres global)

(Tenga en cuenta que MyCorp.TheProduct formaba parte de "3." y, por lo tanto, no era necesario que estuviera entre "4" y "5").

Observaciones finales

No importa si coloca los usos dentro o fuera de la statement del espacio de nombres, siempre existe la posibilidad de que alguien más tarde agregue un nuevo tipo con nombre idéntico a uno de los espacios de nombres que tienen mayor prioridad.

Además, si un espacio de nombres nested tiene el mismo nombre que un tipo, puede causar problemas.

Siempre es peligroso mover los usos de una ubicación a otra porque la jerarquía de búsqueda cambia, y se puede encontrar otro tipo. Por lo tanto, elija una convención y apéguese a ella, para que no tenga que mover los usos.

Las plantillas de Visual Studio, de forma predeterminada, colocan los usos fuera del espacio de nombres (por ejemplo, si hace que VS genere una nueva clase en un archivo nuevo).

Una (pequeña) ventaja de tener utilidades externas es que puede utilizar las directivas using para un atributo global, por ejemplo [assembly: ComVisible(false)] lugar de [assembly: System.Runtime.InteropServices.ComVisible(false)] .

Ponerlo dentro de los espacios de nombres hace que las declaraciones sean locales para ese espacio de nombres para el archivo (en caso de que tenga varios espacios de nombres en el archivo), pero si solo tiene un espacio de nombres por archivo, entonces no importa mucho si salen o no dentro del espacio de nombres

 using ThisNamespace.IsImported.InAllNamespaces.Here; namespace Namespace1 { using ThisNamespace.IsImported.InNamespace1.AndNamespace2; namespace Namespace2 { using ThisNamespace.IsImported.InJustNamespace2; } } namespace Namespace3 { using ThisNamespace.IsImported.InJustNamespace3; } 

De acuerdo con Hanselman – Usando la Directiva y Cargando la Asamblea … y otros artículos similares técnicamente no hay diferencia.

Mi preferencia es ponerlos fuera de los espacios de nombres.

De acuerdo con la documentación de StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNmespace

Causa La directiva de uso de CA # se coloca fuera de un elemento de espacio de nombres.

Descripción de la regla Una violación de esta regla ocurre cuando una directiva using o una directiva de uso de alias se coloca fuera de un elemento de espacio de nombres, a menos que el archivo no contenga ningún elemento de espacio de nombres.

Por ejemplo, el siguiente código resultaría en dos violaciones de esta regla.

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

El siguiente código, sin embargo, no daría lugar a ninguna violación de esta regla:

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

Este código se comstackrá limpiamente, sin ningún error de comstackción. Sin embargo, no está claro qué versión del tipo Guid se está asignando. Si la directiva using se mueve dentro del espacio de nombres, como se muestra a continuación, se producirá un error del comstackdor:

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

El código falla en el siguiente error de comstackción, que se encuentra en la línea que contiene Guid g = new Guid("hello");

CS0576: Espacio de nombres ‘Microsoft.Sample’ contiene una definición que entra en conflicto con alias ‘Guid’

El código crea un alias para el tipo System.Guid llamado Guid, y también crea su propio tipo llamado Guid con una interfaz de constructor correspondiente. Más tarde, el código crea una instancia del tipo Guid. Para crear esta instancia, el comstackdor debe elegir entre las dos definiciones diferentes de Guid. Cuando la directiva using-alias se coloca fuera del elemento de espacio de nombres, el comstackdor elegirá la definición local de Guid definida dentro del espacio de nombres local, e ignorará por completo la directiva de uso de alias definida fuera del espacio de nombres. Esto, desafortunadamente, no es obvio al leer el código.

Sin embargo, cuando la directiva using-alias se coloca dentro del espacio de nombres, el comstackdor debe elegir entre dos tipos de Guid diferentes y conflictivos, ambos definidos dentro del mismo espacio de nombres. Ambos tipos proporcionan un constructor coincidente. El comstackdor no puede tomar una decisión, por lo que marca el error del comstackdor.

La colocación de la directiva de uso de alias fuera del espacio de nombres es una mala práctica, ya que puede generar confusión en situaciones como esta, donde no es evidente qué versión del tipo se está utilizando en realidad. Esto puede conducir a un error que podría ser difícil de diagnosticar.

Colocar directivas de uso de alias dentro del elemento de espacio de nombres elimina esto como una fuente de errores.

  1. Múltiples espacios de nombres

Colocar múltiples elementos de espacio de nombres dentro de un único archivo generalmente es una mala idea, pero si se hace esto, es una buena idea colocar todas las directivas en uso dentro de cada uno de los elementos del espacio de nombres, en lugar de globalmente en la parte superior del archivo. Esto abarcará los espacios de nombres estrechamente, y también ayudará a evitar el tipo de comportamiento descrito anteriormente.

Es importante tener en cuenta que cuando se ha escrito el código con el uso de directivas ubicadas fuera del espacio de nombres, se debe tener cuidado al mover estas directivas dentro del espacio de nombres, para asegurar que esto no cambie la semántica del código. Como se explicó anteriormente, colocar directivas de uso de alias dentro del elemento de espacio de nombres permite al comstackdor elegir entre tipos conflictivos de formas que no ocurrirán cuando las directivas se coloquen fuera del espacio de nombres.

Cómo reparar violaciones Para corregir una violación de esta regla, mueva todas las directivas que usan y las directivas alias using dentro del elemento del espacio de nombres.

Existe un problema al colocar instrucciones de uso dentro del espacio de nombres cuando desea usar alias. El alias no se beneficia de las declaraciones de using anteriores y tiene que estar totalmente calificado.

Considerar:

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

versus:

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

Esto puede ser particularmente pronunciado si tiene un alias prolijo como el siguiente (que es cómo encontré el problema):

 using MyAlias = Tuple>, Expression>>; 

Con el using instrucciones dentro del espacio de nombres, de repente se convierte en:

 using MyAlias = System.Tuple>, System.Linq.Expressions.Expression>>; 

No es bonito.

Como dijo Jeppe Stig Nielsen, este hilo ya tiene excelentes respuestas, pero creo que también vale la pena mencionar esta sutileza bastante obvia.

using directivas especificadas dentro de los espacios de nombres puede hacer que el código sea más corto, ya que no es necesario que estén totalmente calificados, como cuando están especificados en el exterior.

El siguiente ejemplo funciona porque los tipos Foo y Bar están en el mismo espacio de nombre global, Outer .

Supongamos el archivo de código Foo.cs :

 namespace Outer.Inner { class Foo { } } 

Y Bar.cs :

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

Eso puede omitir el espacio de nombre externo en la directiva de using , para abreviar:

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

Otra sutileza que no creo que haya sido cubierta por las otras respuestas es cuando tienes una clase y un espacio de nombres con el mismo nombre.

Cuando tenga la importación dentro del espacio de nombres, encontrará la clase. Si la importación está fuera del espacio de nombres, la importación se ignorará y la clase y el espacio de nombres deberán estar completamente calificados.

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

Las razones técnicas se discuten en las respuestas y creo que al final se trata de las preferencias personales, ya que la diferencia no es tan grande y los intercambios son mutuos. La plantilla predeterminada de Visual Studio para crear archivos .cs usa el using directivas fuera de espacios de nombres, por ejemplo

Uno puede ajustar stylecop para verificar el using directivas fuera de los espacios de nombres agregando el archivo stylecop.json en la raíz del archivo de proyecto con lo siguiente:

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

Puede crear este archivo de configuración en el nivel de solución y agregarlo a sus proyectos como ‘Existing Link File’ para compartir la configuración en todos sus proyectos también.

Es una buena práctica si los que usan por defecto las ” referencias ” utilizadas en su solución de origen deben estar fuera de los espacios de nombres y las que son “nuevas referencias agregadas” es una buena práctica si la coloca dentro del espacio de nombres. Esto es para distinguir qué referencias se están agregando.