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


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

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


1737
2017-09-24 03:49


origen


Respuestas:


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 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 File1 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.Mathy 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 que allí es 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, más bien que Outer.Inner. En ese caso, agregando Outer.Math en File2 rompe File1 independientemente de donde el using va. Esto implica que el compilador busca en el espacio de nombres más interno que lo rodea antes de que mire using directiva.


1839
2017-09-30 02:33



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 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 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 establecer de manera general así: Primero busque el "alcance" más interno para una coincidencia, si no se encuentra nada, salga de un nivel al siguiente alcance y busque allí, y así sucesivamentehasta 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 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 Ambiguous es decir, la búsqueda va en este orden:

  1. Tipos anidados dentro 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.Integrationy 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, busca el tipo Ambiguous va en este orden:

  1. Tipos anidados dentro 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.Integrationy 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 fue parte de "3" y por lo tanto no fue 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 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 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, 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, ponen los usos fuera de del espacio de nombres (por ejemplo, si hace que VS genere una nueva clase en un archivo nuevo).

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


345
2018-04-18 21:00



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

178
2017-09-24 03:52



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

Mi preferencia es ponerlos fuera de los espacios de nombres.


56
2017-09-24 03:53



De acuerdo con la documentación de StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNmespace

Porque Una directiva que usa C # 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 compilará limpiamente, sin ningún error de compilació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 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 de compilació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 compilador debe elegir entre las dos definiciones diferentes de Guid. Cuando la directiva using-alias se coloca fuera del elemento de 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 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 compilador 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 compilador no puede tomar una decisión, por lo que marca el error del compilador.

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 compilador elegir entre tipos conflictivos de formas que no ocurrirán cuando las directivas se coloquen fuera del espacio de nombres.

Cómo arreglar violaciones Para corregir una infracción de esta regla, mueva todas las directivas using y las directivas alias using dentro del elemento namespace.


45
2017-09-14 15:17



Existe un problema al colocar instrucciones de uso dentro del espacio de nombres cuando desea usar alias. El alias no se beneficia de lo 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 prolijo como el siguiente (que es cómo 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.


29
2017-10-10 18:47



Como Jeppe Stig Nielsen dijo, este hilo ya tiene excelentes respuestas, pero pensé que esta sutileza bastante obvia era digna de mención también.

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

El siguiente ejemplo funciona porque los tipos Foo y Bar están ambos en el mismo espacio de nombre 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 nombre externo en el using directiva, para abreviar:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

2
2017-09-17 10:32



Las razones técnicas se discuten en las respuestas y creo que finalmente se trata de las preferencias personales ya que la diferencia no es grande y hay compensaciones para ambos. La plantilla predeterminada de Visual Studio para crear .csuso de archivos using directivas fuera de espacios de nombres, p.

Uno puede ajustar stylecop para verificar using directivas fuera de los espacios de nombres mediante la adición stylecop.json archivo 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.


0
2018-06-03 12:38