Pregunta

Yo he estado corriendo EstiloCop sobre algún código C#, y sigue informando que mi using Las directivas deben estar dentro del espacio de nombres.

¿Existe alguna razón técnica para poner el using ¿Directivas dentro en lugar de fuera del espacio de nombres?

¿Fue útil?

Solución

En realidad, existe 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 imagina que alguien agrega otro archivo (File2.cs) al proyecto que se ve así:

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

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

Esto cambia si pones el using dentro de su declaración 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 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 una en System;El punto aquí es simplemente que hay es 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 vez de Outer.Inner.En ese caso, añadiendo Outer.Math en Archivo2 rompe Archivo1 independientemente de dónde esté using va.Esto implica que el compilador busca el espacio de nombres más interno antes de mirar cualquier using directiva.

Otros consejos

Este hilo ya tiene algunas respuestas excelentes, 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 totalmente equivalente a:

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

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

La regla para resolver qué tipo está implícito se puede expresar de la siguiente manera: Primero busque una coincidencia en el "alcance" más interno; si no encuentra nada, vaya 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 es del ensamblado actual, elija ese y emita una advertencia del compilador.De lo contrario, abandone (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 exteriores:

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 saber qué tipo Ambiguous es decir, la búsqueda va en este orden:

  1. Tipos anidados en el interior C (incluidos los tipos anidados heredados)
  2. Tipos en el espacio de nombres 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 nulo espacio de nombres (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 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:

  1. Tipos anidados en el interior C (incluidos los tipos anidados heredados)
  2. Tipos en el espacio de nombres 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 nulo espacio de nombres (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 del espacio de nombres, siempre existe la posibilidad de que alguien agregue más tarde un nuevo tipo con nombre idéntico a uno de los espacios de nombres que tiene 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 es posible que se encuentre otro tipo.Por lo tanto, elija una convención y cúmplala, de modo que nunca tenga que cambiar de uso.

Las plantillas de Visual Studio, de forma predeterminada, tienen los usos afuera 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 afuera es que luego puede utilizar las directivas de uso para un atributo global, por ejemplo [assembly: ComVisible(false)] en 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 del 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 hay mucha diferencia si salen o 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 a Hanselman - Uso de directivas y ensamblaje Cargando... 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:Las directivas de uso deben colocarse dentro del espacio de nombres

Cause AC# Uso de la Directiva 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 de uso de Using o Using-Alias ​​se coloca fuera de un elemento de espacio de nombres, a menos que el archivo no contenga ningún elemento del espacio de nombres.

Por ejemplo, el siguiente código daría lugar a dos infracciones de esta regla.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

Sin embargo, el siguiente código no daría lugar a ninguna infracció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 asigna.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 que entra 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 coincidente.Posteriormente, 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 usando-alias 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á por completo la directiva usando-alias definida fuera del espacio de nombres.Desafortunadamente, esto no es obvio al leer el código.

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

Colocar la directiva using-alias fuera del espacio de nombres es una mala práctica porque puede generar confusión en situaciones como ésta, donde no es obvio qué versión del tipo se está utilizando realmente.Potencialmente, esto puede provocar un error que podría ser difícil de diagnosticar.

Colocar directivas using-alias dentro del elemento del espacio de nombres elimina esto como fuente de errores.

  1. Múltiples espacios de nombres

Colocar múltiples elementos de espacio de nombres dentro de un solo archivo es generalmente 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 globalmente en la parte superior del archivo.Esto limitará el alcance de los espacios de nombres y también ayudará a evitar el tipo de comportamiento descrito anteriormente.

Es importante tener en cuenta que cuando el código se escribe utilizando directivas colocadas fuera del espacio de nombres, se debe tener cuidado al mover estas directivas dentro del espacio de nombres, para garantizar que esto no cambie la semántica del código.Como se explicó anteriormente, colocar directivas using-alias dentro del elemento del espacio de nombres permite al compilador elegir entre tipos en conflicto de maneras que no sucederán cuando las directivas se coloquen fuera del espacio de nombres.

Cómo arreglar las violaciones para solucionar una violación de esta regla, mueva todas las directivas de uso y las directivas de uso de Alias ​​dentro del elemento del espacio de nombres.

Existe un problema al colocar declaraciones de uso dentro del espacio de nombres cuando desea utilizar alias.El alias no se beneficia del anterior. using declaraciones 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 largo como el siguiente (que es como encontré el problema):

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

Con using declaraciones 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.

Como Jeppe Stig Nielsen dicho, este hilo ya tiene excelentes respuestas, pero pensé que también valía la pena mencionar esta sutileza bastante obvia.

using Las directivas especificadas dentro de los espacios de nombres pueden generar un código más corto, ya que no necesitan estar completamente calificados como cuando se especifican en el exterior.

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

Supongamos que 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 exterior en el using directiva, para abreviar:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Una arruga con la que me encontré (que no está cubierta en otras respuestas):

Supongamos que tiene estos espacios de nombres:

  • Algún otro
  • Padre.Algo.Otro

cuando usas using Something.Other afuera de un namespace Parent, se refiere al primero (Algo.Otro).

Sin embargo si lo usas adentro de esa declaración de espacio de nombres, se refiere al segundo (Parent.Something.Other)!

Existe una solución simple:añade el "global::"prefijo: documentos

namespace Parent
{
   using global::Something.Other;
   // etc
}

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 se realiza fuera del espacio de nombres, 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 preferencias personales, ya que la diferencia no es esa. grande y hay compensaciones para ambos.Plantilla predeterminada de Visual Studio para crear .cs uso de archivos using directivas fuera de espacios de nombres, p.

Se puede ajustar stylecop para comprobar using directivas fuera de los espacios de nombres mediante la adición stylecop.json archivo 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 solución y agregarlo a sus proyectos como 'Archivo de enlace existente' para compartir la configuración en todos sus proyectos también.

Es una mejor práctica si esos por defecto usando es decir"referencias"utilizado en su solución fuente debe estar fuera de los espacios de nombres y aquellos que están "nueva referencia agregada" Una buena práctica es colocarlo dentro del espacio de nombres.Esto es para distinguir qué referencias se están agregando.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top