Pergunta

Fui correndo StyleCop sobre algum código C #, e ele continua relatando que minhas directivas using deve estar dentro do espaço de nomes.

Existe uma razão técnica para colocar as directivas using dentro em vez de fora do namespace?

Foi útil?

Solução

Há realmente uma diferença (subtil) entre os dois. Imagine que você tenha o seguinte código no File1.cs:

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

Agora imagine que alguém adiciona outro arquivo (File2.cs) para o projeto que se parece com isso:

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

As pesquisas compilador Outer antes de olhar para essas directivas using fora do espaço de nomes, por isso encontra Outer.Math vez de System.Math. Infelizmente (ou felizmente?), Outer.Math tem nenhum membro PI, assim File1 agora está quebrado.

Isso muda se você colocar o using dentro de sua declaração de namespace, da seguinte forma:

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

Agora, as pesquisas compilador System antes de pesquisar Outer, achados System.Math, e está tudo bem.

Alguns argumentam que Math pode ser um mau nome para uma classe definida pelo usuário, uma vez que já existe um em System; o ponto aqui é apenas que há é a diferença, e isso afeta a manutenção do seu código.

É também interessante notar o que acontece se Foo está em Outer namespace, ao invés de Outer.Inner. Nesse caso, acrescentando Outer.Math em File2 quebra File1 independentemente de onde o using vai. Isto implica que as pesquisas do compilador o mais íntimo encerrando namespace antes que olha para qualquer directiva using.

Outras dicas

Esta discussão já tem alguns grandes respostas, mas eu sinto que eu posso trazer um pouco mais detalhadamente com esta resposta adicional.

Em primeiro lugar, lembre-se que uma declaração de namespace com períodos, como:

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

é inteiramente equivalente a:

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

Se você quisesse, você poderia colocar directivas using em todos esses níveis. (Claro, nós queremos ter usings em apenas um lugar, mas seria legal de acordo com o idioma.)

A regra para resolver que tipo é implícita, pode ser vagamente indicado como este: Primeiro procurar o mais interior "escopo" para uma partida, se nada for encontrado lá sair um nível para o seguinte escopo e pesquisa lá, e assim por diante , até que seja encontrada uma correspondência. Se em algum nível mais do que uma correspondência for encontrada, se um dos tipos são da montagem atual, escolher aquele e emitir um aviso do compilador. Caso contrário, dar-se (erro de tempo de compilação).

Agora, vamos ser explícito sobre o que isso significa em um exemplo concreto com as duas principais convenções.

(1) Com usings fora:

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

No caso acima, para descobrir que tipo Ambiguous é, a busca continua, nesta ordem:

  1. tipos aninhados dentro C (incluindo tipos aninhados herdadas)
  2. Tipos no MyCorp.TheProduct.SomeModule.Utilities atual namespace
  3. Tipos de namespace MyCorp.TheProduct.SomeModule
  4. Tipos em MyCorp.TheProduct
  5. Tipos em MyCorp
  6. Tipos no nulo namespace (o namespace global)
  7. Tipos em System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration e ThirdParty

A outra convenção:

(2) Com usings 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;
    }
}

Agora, procurar o tipo Ambiguous vai nesta ordem:

  1. tipos aninhados dentro C (incluindo tipos aninhados herdadas)
  2. Tipos no MyCorp.TheProduct.SomeModule.Utilities atual namespace
  3. Tipos em System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration e ThirdParty
  4. Tipos de namespace MyCorp.TheProduct.SomeModule
  5. Tipos em MyCorp
  6. Tipos no nulo namespace (o namespace global)

(Note que MyCorp.TheProduct era uma parte de "3" e foi, portanto, não é necessário entre "4" e "5".).

Observações finais

Não importa se você colocar os usings dentro ou fora da declaração de namespace, há sempre a possibilidade de que alguém mais tarde adiciona um novo tipo com nome idêntico a um dos namespaces que têm maior prioridade.

Além disso, se um namespace aninhado tem o mesmo nome como um tipo, ele pode causar problemas.

É sempre perigoso para mover os usings de um local para outro, porque a hierarquia de busca mudanças, e outro tipo pode ser encontrada. Portanto, escolha uma convenção e cumpri-lo, de modo que você não terá que nunca mover usings.

Visual modelos de estúdio, por padrão, coloque as usings fora do namespace (por exemplo, se você fizer VS gerar uma nova classe em um novo arquivo).

One (minúsculo) vantagem de ter usings fora é que você pode, então, utilizar as directivas que utilizam para um atributo global, por exemplo [assembly: ComVisible(false)] vez de [assembly: System.Runtime.InteropServices.ComVisible(false)].

Colocá-lo dentro dos namespaces faz as declarações locais para que namespace para o arquivo (no caso de você ter vários namespaces no arquivo), mas se você só tem um namespace por arquivo, em seguida, não faz muita diferença se eles ir para fora ou dentro do espaço de nomes.

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 acordo com a Hanselman - Usando directiva e Assembléia Carregando ... e outros tais artigos não é tecnicamente nenhuma diferença.

A minha preferência é colocá-los fora de namespaces.

De acordo com o de Documentação StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Causa A C # usando diretiva é colocado fora de um elemento de namespace.

Descrição da regra A violação desta regra ocorre quando usando uma diretiva ou uma directiva usando-alias é colocado fora de um elemento de namespace, a menos que o arquivo não contém quaisquer elementos de namespace.

Por exemplo, o código a seguir resultaria em duas violações desta regra.

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

O código a seguir, no entanto, não resultou em quaisquer violações desta regra:

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

    public class Program
    {
    }
}

Este código irá compilar de forma limpa, sem erros do compilador. No entanto, não está claro qual versão do tipo Guid está sendo alocado. Se a diretiva usando é movido dentro do espaço de nomes, como mostrado abaixo, um erro do compilador irá ocorrer:

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

O código de falha no seguinte erro do compilador, encontrado na linha contendo Guid g = new Guid("hello");

CS0576: Namespace 'Microsoft.Sample' contém uma definição em conflito com o apelido 'Guid'

O código cria um alias para o tipo System.Guid chamado Guid, e também cria seu próprio tipo chamado Guid com uma interface construtor correspondente. Mais tarde, o código cria uma instância do tipo Guid. Para criar este exemplo, o compilador deve escolher entre as duas definições diferentes de Guid. Quando a directiva usando-alias é colocado fora do elemento namespace, o compilador vai escolher a definição local de Guid definida dentro do namespace local, e ignorar completamente a directiva usando-Alias ??definido fora do espaço de nomes. Isso, infelizmente, não é óbvio quando a leitura do código.

Quando a directiva usando-alias é posicionado no interior do espaço de nomes, no entanto, o compilador tem de escolher entre dois diferentes, conflitantes tipos Guid ambos definidos no mesmo espaço. Ambos os tipos fornecer um construtor correspondente. O compilador é incapaz de tomar uma decisão, então ele sinaliza o erro do compilador.

A colocação do utilizando-Alias ??diretiva fora do espaço de nomes é uma prática ruim, pois pode levar a confusão em situações como esta, onde não é óbvio qual versão do tipo está realmente sendo usado. Isto pode potencialmente levar a um bug que pode ser difícil de diagnosticar.

Colocar directivas usando-alias dentro do elemento namespace elimina esta como uma fonte de bugs.

  1. Namespaces múltiplas

Colocando múltiplos elementos de namespace em um único arquivo é geralmente uma má idéia, mas se e quando isso é feito, é uma boa idéia de colocar todas as directivas que usam dentro de cada um dos elementos de nomes, em vez de globalmente no topo da Arquivo. Este escopo vontade os namespaces com força, e também ajuda a evitar o tipo de comportamento descrito acima.

É importante notar que quando o código foi escrito com o uso de directivas colocados fora do espaço de nomes, o cuidado deve ser tomado quando se deslocam estas directivas dentro do espaço de nomes, para garantir que isso não está mudando a semântica do código. Como explicado acima, colocando directivas usando-alias dentro do elemento namespace permite que o compilador para escolher entre conflitantes tipos de formas que não vai acontecer quando as directivas são colocados fora do espaço de nomes.

Como corrigir violações Para corrigir uma violação desta regra, mover todos usando directivas e usando-alias directivas dentro do elemento namespace.

Há um problema com a colocação usando instruções dentro do espaço de nomes quando você deseja usar aliases. O alias não beneficiar das declarações using anteriores e tem de ser totalmente qualificado.

Considere o seguinte:

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

    class MyClass
    {
    }
}

contra:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Isto pode ser particularmente pronunciado se você tiver um alias prolixo como o seguinte (que é como eu encontrei o problema):

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

Com declarações using dentro do espaço de nomes, de repente torna-se:

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

Não é bonito.

Como Jeppe Stig Nielsen disse , esta discussão já tem grandes respostas, mas eu pensei que essa sutileza bastante óbvio valia a pena mencionar também.

directivas using especificados dentro namespaces pode fazer para o código mais curto, uma vez que não precisa ser totalmente qualificado como quando eles estão especificados na parte externa.

O exemplo a seguir funciona porque os tipos Foo e Bar estão ambos no mesmo espaço de nomes global, Outer.

presumo o arquivo de código Foo.cs :

namespace Outer.Inner
{
    class Foo { }
}

e Bar.cs :

namespace Outer
{
    using Outer.Inner;

    class Bar
    {
        public Foo foo;
    }
}

Isso pode omitir o namespace externo na directiva using, para breve:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}

Uma ruga Corri para (que não é coberto em outras respostas):

Suponha que você tenha esses namespaces:

  • Something.Other
  • Parent.Something.Other

Quando você usa using Something.Other fora de um namespace Parent, refere-se ao primeiro (Something.Other).

No entanto, se você usá-lo dentro dessa declaração namespace, refere-se a segunda (Parent.Something.Other)!

Não há uma solução simples: adicione o prefixo "global::": docs

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

Outra sutileza que eu não acredito que tenha sido coberto pelas outras respostas é para quando você tem uma classe e namespace com o mesmo nome.

Quando você tem a importação dentro do namespace, então ele irá encontrar a classe. Se a importação for fora do espaço de nomes, em seguida, a importação será ignorado e a classe e namespace tem que ser totalmente qualificado.

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

As razões técnicas são discutidas nas respostas e acho que se trata de preferências pessoais, no final, uma vez que a diferença não é tão grandes e há vantagens e desvantagens para ambos. modelo padrão do Visual Studio para criar arquivos .cs usar directivas using fora de namespaces por exemplo.

Pode-se ajustar o StyleCop para verificar directivas using fora de namespaces através da adição de arquivos stylecop.json na raiz do arquivo de projeto com o seguinte:

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

Você pode criar esse arquivo de configuração no nível da solução e adicioná-la a seus projetos como 'existente Link de arquivo' para compartilhar a configuração em todos os seus projetos também.

É uma prática melhor se os padrão usando ou seja, " referências " usado em sua solução de código deve estar fora dos namespaces e aqueles que são "novo adicionado referência " é uma boa prática é que você deve colocá-lo dentro do espaço de nomes. Isso é para distinguir o que referências estão sendo adicionados.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top