c# - tipo - ¿Deben las directivas ''usar'' estar dentro o fuera del espacio de nombres?
no se puede encontrar el tipo o el nombre de espacio de nombres c# (10)
Como said Jeppe Stig Nielsen, este hilo ya tiene grandes respuestas, pero pensé que también valía 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 completamente calificados cuando se especifican en el exterior.
El siguiente ejemplo funciona porque los tipos Foo
y Bar
están en el mismo espacio de nombres global, Outer
.
Presume 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 nombres externo en la directiva using
, para abreviar:
namespace Outer
{
using Inner;
class Bar
{
public Foo foo;
}
}
He estado ejecutando StyleCop sobre un código C #, y sigue informando que mis directivas de using
deben estar dentro del espacio de nombres.
¿Hay alguna razón técnica para colocar las directivas de using
en lugar de fuera del espacio de nombres?
En realidad, hay una diferencia (sutil) entre los dos. Imagina que tienes 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 compilador busca en Outer
antes de ver las 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 PI
, por lo que File1 ahora está dañado.
Esto cambia si pones el using
dentro de tu declaración de espacio de nombres, como sigue:
// File1b.cs
namespace Outer.Inner
{
using System;
class Foo
{
static void Bar()
{
double d = Math.PI;
}
}
}
Ahora el compilador busca System
antes de buscar Outer
, encuentra System.Math
y todo está bien.
Algunos dirían que Math
podría ser un mal nombre para una clase definida por el usuario, ya que ya hay uno en el System
; el punto aquí es que hay una diferencia y afecta la capacidad de mantenimiento de su código.
También es interesante observar qué sucede si Foo
está en el espacio de nombres Outer
, en lugar de Outer.Inner
. En ese caso, la adición de Outer.Math
en File2 rompe File1 independientemente de a dónde vaya el using
. Esto implica que el compilador busca el espacio de nombres que lo contiene más interno antes de mirar cualquier directiva de using
.
Es una mejor práctica si los valores predeterminados que usan " referencias " usadas en su solución de origen deben estar fuera de los espacios de nombres y aquellos que son "nuevas referencias agregadas" es una buena práctica, es ponerlo dentro del espacio de nombres. Esto es para distinguir qué referencias se están agregando.
Este hilo ya tiene algunas respuestas geniales, pero creo que puedo aportar un poco más de detalles con esta respuesta adicional.
Primero, recuerde que una declaración 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 lo desea, puede using
directivas en todos estos niveles. (Por supuesto, queremos tener s en un solo lugar, pero sería legal de acuerdo con el idioma).
La regla para resolver qué tipo está implícito, se puede expresar de esta manera: primero, busque el "alcance" más interno para buscar una coincidencia, si no encuentra nada allí, salga de un nivel al siguiente alcance 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 pertenece al ensamblaje actual, selecciónelo y emita una advertencia del compilador. De lo contrario, renunciar (error en tiempo de compilació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 de Ambiguous
es, la búsqueda continúa en este orden:
- Tipos anidados dentro de
C
(incluidos los tipos anidados heredados) - Tipos en el espacio de nombres actual
MyCorp.TheProduct.SomeModule.Utilities
- Tipos en el espacio de nombres
MyCorp.TheProduct.SomeModule
- Tipos en
MyCorp.TheProduct
- Tipos en
MyCorp
- Escribe en el espacio de nombres nulo (el espacio de nombres global)
- Tipos en
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
yThirdParty
La otra convención:
(2) Con usos en el interior:
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, busca el tipo Ambiguous
va en este orden:
- Tipos anidados dentro de
C
(incluidos los tipos anidados heredados) - Tipos en el espacio de nombres actual
MyCorp.TheProduct.SomeModule.Utilities
- Tipos en
System
,System.Collections.Generic
,System.Linq
,MyCorp.TheProduct
,MyCorp.TheProduct.OtherModule
,MyCorp.TheProduct.OtherModule.Integration
, yThirdParty
- Tipos en el espacio de nombres
MyCorp.TheProduct.SomeModule
- Tipos en
MyCorp
- Escribe en el espacio de nombres nulo (el espacio de nombres global)
(Tenga en cuenta que MyCorp.TheProduct
era parte de "3" y, por lo tanto, no era necesario entre "4" y "5".)
Observaciones finales
No importa si coloca los usos dentro o fuera de la declaración de espacio de nombres, siempre existe la posibilidad de que alguien más adelante 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 anidado 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, elige una convención y apégate a ella, para que no tengas que moverte nunca.
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 nuevo archivo).
Una (pequeña) ventaja de tener usos externos es que luego puede utilizar las directivas de uso para un atributo global, por ejemplo [assembly: ComVisible(false)]
lugar de [assembly: System.Runtime.InteropServices.ComVisible(false)]
.
Existe un problema con la ubicación de las declaraciones de uso dentro del espacio de nombres cuando desea utilizar alias. El alias no se beneficia de las declaraciones de using
anteriores y debe estar completamente 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 de mucho aliento como el siguiente (que es cómo encontré el problema):
using MyAlias = Tuple<Expression<Func<DateTime, object>>, Expression<Func<TimeSpan, object>>>;
Con el using
sentencias dentro del espacio de nombres, de repente se convierte en:
using MyAlias = System.Tuple<System.Linq.Expressions.Expression<System.Func<System.DateTime, object>>, System.Linq.Expressions.Expression<System.Func<System.TimeSpan, object>>>;
No es bonito.
Las razones técnicas se discuten en las respuestas y creo que se trata de las preferencias personales al final, ya que la diferencia no es tan grande y hay concesiones para ambos. La plantilla predeterminada de Visual Studio para crear archivos .cs
using
directivas fuera de los espacios de nombres, por ejemplo
Se puede ajustar el stylecop para verificar el using
directivas fuera de los espacios de nombres mediante la adición del archivo stylecop.json
en la raíz del archivo del 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 la solución y agregarlo a sus proyectos como ''Archivo de enlace existente'' para compartir la configuración en todos sus proyectos también.
Otra sutileza que no creo haya sido cubierta por las otras respuestas es para cuando tiene una clase y un espacio de nombres con el mismo nombre.
Cuando tenga la importación dentro del espacio de nombres, entonces 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.
}
}
}
Ponerlo dentro de los espacios de nombres hace que las declaraciones sean locales a ese espacio de nombres para el archivo (en caso de que tenga múltiples espacios de nombres en el archivo), pero si solo tiene un espacio de nombres por archivo, entonces no hay mucha diferencia, ya sea que salgan 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;
}
Según Hanselman - Uso de directivas y carga de ensamblajes ... y otros artículos similares, técnicamente no hay diferencia.
Mi preferencia es ponerlos fuera de los espacios de nombres.
Según la documentación de StyleCop:
SA1200: UsingDirectivesMustBePlacedWithinNamespace
Causa La directiva de uso de AC # se coloca fuera de un elemento de espacio de nombres.
Descripción de la regla Una violación de esta regla se produce cuando una directiva de uso o una directiva de alias de uso 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 daría como resultado 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 como resultado ninguna violación de esta regla:
namespace Microsoft.Sample
{
using System;
using Guid = System.Guid;
public class Program
{
}
}
Este código se compilará limpiamente, sin errores de compilación. Sin embargo, no está claro qué versión del tipo Guid se está asignando. Si la directiva de uso se mueve dentro del espacio de nombres, como se muestra a continuación, se producirá un error del compilador:
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 del compilador, que se encuentra en la línea que contiene Guid g = new Guid("hello");
CS0576: El espacio de nombres ''Microsoft.Sample'' contiene una definición en conflicto con el 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 compilador debe elegir entre las dos definiciones diferentes de Guid. Cuando la directiva de alias de uso se coloca fuera del elemento del espacio de nombres, el compilador elegirá la definición local de Guid definida dentro del espacio de nombres local e ignorará completamente la directiva de alias de uso definida fuera del espacio de nombres. Esto, desafortunadamente, no es obvio al leer el código.
Sin embargo, cuando la directiva de uso de alias se coloca dentro del espacio de nombres, el compilador debe elegir entre dos tipos de Guid diferentes y en conflicto, ambos definidos dentro del mismo espacio de nombres. Ambos de estos tipos proporcionan un constructor coincidente. El compilador no puede tomar una decisión, por lo que marca el error del compilador.
Colocar la directiva de alias de uso fuera del espacio de nombres es una mala práctica porque puede generar confusión en situaciones como esta, donde no es obvio qué versión del tipo se está utilizando realmente. Esto puede conducir potencialmente a un error que podría ser difícil de diagnosticar.
La colocación de directivas de alias de uso dentro del elemento del espacio de nombres elimina esto como una fuente de errores.
- Múltiples espacios de nombres
Por lo general, colocar varios elementos del espacio de nombres en un solo archivo es una mala idea, pero si se hace esto, es una buena idea colocar todas las directivas de uso dentro de cada uno de los elementos del espacio de nombres, en lugar de ubicarse 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 un código utilizando directivas ubicadas fuera del espacio de nombres, se debe tener cuidado al mover estas directivas dentro del espacio de nombres, para asegurarse de que esto no cambie la semántica del código. Como se explicó anteriormente, colocar directivas de alias de uso dentro del elemento del espacio de nombres le permite al compilador elegir entre tipos en conflicto de una manera que no sucederá cuando las directivas estén ubicadas fuera del espacio de nombres.
Cómo corregir las infracciones Para corregir una infracción de esta regla, mueva todas las directivas de uso y las directivas de alias de uso dentro del elemento de espacio de nombres.