As diretivas 'using' devem estar dentro ou fora do namespace?

2062

Estou executando o StyleCop sobre algum código C # e ele continua relatando que minhas usingdiretivas devem estar dentro do espaço para nome.

Existe uma razão técnica para colocar as usingdiretivas dentro e não fora do espaço para nome?

benPearce
fonte
4
Às vezes faz diferença onde você coloca usings: stackoverflow.com/questions/292535/linq-to-sql-designer-bug
gius
82
Apenas para referência, existem implicações além da questão de várias classes por arquivo; portanto, se você é novo nessa questão, continue lendo.
Charlie
3
@ user-12506 - isso não funciona muito bem em uma equipe de desenvolvimento de médio a grande porte, em que é necessário algum nível de consistência de código. E, como observado anteriormente, se você não entender os diferentes layouts, poderá encontrar casos extremos que não funcionam conforme o esperado.
21814 benPearce
35
Terminologia: Essas não são using declarações ; eles são using diretrizes . Uma usingdeclaração, por outro lado, é uma estrutura de linguagem que ocorre junto com outras declarações dentro de um corpo de método etc. Como exemplo, using (var e = s.GetEnumerator()) { /* ... */ }é uma declaração que é vagamente a mesma que var e = s.GetEnumerator(); try { /* ... */ } finally { if (e != null) { e.Dispose(); } }.
Jeppe Stig Nielsen
1
Se isso não foi mencionado por ninguém, na verdade Microsoft também recomenda colocar usingdeclarações no interior das namespacedeclarações, em suas guidlines codificação internos
user1451111

Respostas:

2133

Na verdade, existe uma diferença (sutil) entre os dois. Imagine que você tenha o seguinte código em File1.cs:

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

Agora imagine que alguém adicione outro arquivo (File2.cs) ao projeto que se parece com isso:

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

O compilador pesquisa Outerantes de olhar para essas usingdiretivas fora do espaço para nome, para encontrar em Outer.Mathvez de System.Math. Infelizmente (ou talvez felizmente?), Não Outer.Mathtem PImembro, então o File1 está quebrado agora.

Isso muda se você colocar usingdentro da sua declaração de namespace, da seguinte maneira:

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

Agora, o compilador pesquisa Systemantes de pesquisar Outer, encontra System.Mathe está tudo bem.

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

Também é interessante observar o que acontece se Fooestiver no espaço para nome Outer, e não Outer.Inner. Nesse caso, a adição Outer.Mathno Arquivo2 interrompe o Arquivo1, independentemente de para onde usingvai. Isso implica que o compilador pesquisa o espaço para nome mais interno antes de examinar qualquer usingdiretiva.

Charlie
fonte
28
Esse é um motivo muito melhor para colocar usando instruções localmente do que o argumento de vários espaços de nomes em um arquivo de Mark. Especialmente, a compilação pode e irá reclamar sobre o conflito de nomes (consulte a documentação do StyleCop para esta regra (por exemplo, conforme publicado por Jared)).
David Schmitt
148
A resposta aceita é boa, mas para mim parece uma boa razão para colocar as cláusulas using fora do namespace. Se eu estiver no espaço para nome Outer.Inner, esperaria que ele usasse a classe Math de Outer.Inner e não System.Math.
Frank Wallis
7
Eu concordo com isso também. A resposta aceita está correta, na medida em que descreve tecnicamente a diferença. No entanto, uma ou outra classe precisará de uma chamada explícita. Eu recomendaria muito que "Math" resolvesse minha própria classe local e "System.Math" se referisse à classe externa - mesmo se System.Math estivesse sendo usado como "Math" antes de Outer.Math existir. Sim, é mais trabalhoso corrigir no entanto muitas referências pré-existentes, mas isso também pode ser uma dica de que talvez Outer.Math deva ter um nome diferente!
mbmcavoy
13
Ótima resposta, mas me parece que eu gostaria apenas de colocar localizações não-framework usando localmente, e manter o framework global usando declarações. Alguém tem mais explicações sobre por que eu deveria mudar completamente minha preferência? Também de onde isso veio, os modelos no VS2008 colocados fora do espaço para nome?
Timina 04/04
31
Eu acho que isso é mais uma convenção de nomenclatura incorreta do que mudar o local do seu uso. Não deve haver uma classe chamada de matemática em sua solução
JDeveloper
454

Esse tópico já tem ótimas respostas, mas acho que posso trazer um pouco mais de detalhes com essa resposta adicional.

Primeiro, lembre-se de uma declaração de espaço para nome com pontos, como:

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

é inteiramente equivalente a:

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

Se você quisesse, poderia colocar usingdiretivas em todos esses níveis. (É claro que queremos ter usings em apenas um lugar, mas seria legal de acordo com o idioma.)

A regra para resolver qual tipo está implícito pode ser declarada de maneira vaga: Primeiro, procure uma correspondência correspondente no "escopo" mais interno; se nada for encontrado lá, vá um nível para o próximo escopo e procure lá, e assim por diante , até que uma correspondência seja encontrada. Se em algum nível mais de uma correspondência for encontrada, se um dos tipos pertencer à montagem atual, escolha essa e emita um aviso do compilador. Caso contrário, desista (erro em tempo de compilação).

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

(1) Com utilizações externas:

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 qual Ambiguousé o tipo , a pesquisa segue nesta ordem:

  1. Tipos aninhados dentro C(incluindo tipos aninhados herdados)
  2. Tipos no espaço para nome atual MyCorp.TheProduct.SomeModule.Utilities
  3. Tipos no espaço para nome MyCorp.TheProduct.SomeModule
  4. Tipos em MyCorp.TheProduct
  5. Tipos em MyCorp
  6. Tipos no espaço para nome nulo (o espaço para nome global)
  7. Tipos em System, System.Collections.Generic, System.Linq, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, eThirdParty

A outra convenção:

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

Agora, procure o tipo Ambiguousnesta ordem:

  1. Tipos aninhados dentro C(incluindo tipos aninhados herdados)
  2. Tipos no espaço para nome atual MyCorp.TheProduct.SomeModule.Utilities
  3. Tipos em System, System.Collections.Generic, System.Linq, MyCorp.TheProduct, MyCorp.TheProduct.OtherModule, MyCorp.TheProduct.OtherModule.Integration, eThirdParty
  4. Tipos no espaço para nome MyCorp.TheProduct.SomeModule
  5. Tipos em MyCorp
  6. Tipos no espaço para nome nulo (o espaço para nome global)

(Observe que isso MyCorp.TheProductfazia parte do "3." e, portanto, não era necessário entre "4." e "5.".)

Observações finais

Não importa se você coloca as utilizações dentro ou fora da declaração do espaço para nome, sempre há a possibilidade de alguém posteriormente adicionar um novo tipo com nome idêntico a um dos espaços para nome com prioridade mais alta.

Além disso, se um espaço para nome aninhado tiver o mesmo nome que um tipo, poderá causar problemas.

É sempre perigoso mover os usos de um local para outro, porque a hierarquia de pesquisa é alterada e outro tipo pode ser encontrado. Portanto, escolha uma convenção e cumpra-a, para que você nunca precise mudar de uso.

Os modelos do Visual Studio, por padrão, colocam as utilizações fora do espaço para nome (por exemplo, se você fizer o VS gerar uma nova classe em um novo arquivo).

Uma (pequena) vantagem de ter utilizações externas é que você pode utilizar as diretivas de uso de um atributo global, por exemplo, em [assembly: ComVisible(false)]vez de [assembly: System.Runtime.InteropServices.ComVisible(false)].

Jeppe Stig Nielsen
fonte
46
Essa é a melhor explicação, porque destaca o fato de que a posição das instruções 'using' é uma decisão deliberada do desenvolvedor. Em nenhum caso, alguém deve descuidadamente alterar a localização das declarações 'using' sem entender as implicações. Portanto, a regra StyleCop é simplesmente burra.
ZunTzu 8/01/16
194

Colocá-lo dentro dos namespaces torna as declarações locais nesse namespace para o arquivo (caso você tenha vários namespaces no arquivo), mas se você tiver apenas um namespace por arquivo, não fará muita diferença se elas vão para fora ou dentro do espaço para nome.

using ThisNamespace.IsImported.InAllNamespaces.Here;

namespace Namespace1
{ 
   using ThisNamespace.IsImported.InNamespace1.AndNamespace2;

   namespace Namespace2
   { 
      using ThisNamespace.IsImported.InJustNamespace2;
   }       
}

namespace Namespace3
{ 
   using ThisNamespace.IsImported.InJustNamespace3;
}
Mark Cidade
fonte
namespaces fornecem uma separação lógica, não física (arquivo).
Jowen
9
Não é bem verdade que não há diferença; usingdiretivas dentro de namespaceblocos podem se referir a namespaces relativos com base no namespacebloco anexo .
OR Mapper
70
sim, eu sei. estabelecemos que, na resposta aceita dessa pergunta, cinco anos atrás.
Mark Cidade
59

De acordo com Hanselman - Usando o carregamento de diretivas e montagens ... e outros artigos desse tipo, tecnicamente não há diferença.

Minha preferência é colocá-los fora dos espaços para nome.

Quintin Robinson
fonte
3
@ Chris M: uh ... o link postado na resposta indica que há nenhum benefício no vs. fora, realmente mostrando um exemplo que falsifica a afirmação feita no link que você postou ...
johnny
2
Sim, eu não li completamente o tópico, mas comprei quando os MVPs disseram que estava certo. Um cara refuta, explica e mostra seu código mais adiante ... "A IL que o compilador C # gera é a mesma em ambos os casos. Na verdade, o compilador C # gera precisamente nada correspondente a cada diretiva de uso. O uso de diretivas é puramente um C # ism, e eles não têm significado para o próprio .NET. (Não é verdade para usar instruções, mas essas são algo bem diferente.) " Groups.google.com/group/wpf-disciples/msg/781738deb0a15c46
Chris McKee
84
Por favor inclua um resumo do link. Quando a ligação é interrompida (porque vai acontecer, dado o tempo suficiente), de repente, uma resposta com 32 upvotes só vale a pena My style is to put them outside the namespaces.- apenas uma resposta em tudo.
ANVES
11
A alegação aqui está simplesmente errada ... há uma diferença técnica e sua própria citação diz isso ... de fato, é disso que se trata. Por favor, exclua esta resposta errada ... existem respostas muito melhores e precisas.
Jim Balter
53

De acordo com a documentação do StyleCop:

SA1200: UsingDirectivesMustBePlacedWithinNamespace

Causa O AC # usando diretiva é colocado fora de um elemento de espaço para nome.

Descrição da regra Uma violação desta regra ocorre quando uma diretiva usando ou diretiva usando alias é colocada fora de um elemento do espaço para nome, a menos que o arquivo não contenha nenhum elemento do espaço para nome.

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

using System;
using Guid = System.Guid;

namespace Microsoft.Sample
{
    public class Program
    {
    }
}

O código a seguir, no entanto, não resultaria em nenhuma violação desta regra:

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

    public class Program
    {
    }
}

Esse código será compilado de maneira limpa, sem erros do compilador. No entanto, não está claro qual versão do tipo Guid está sendo alocada. Se a diretiva using for movida para dentro do namespace, como mostrado abaixo, ocorrerá um erro do 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");
        }
    }
}

O código falha no seguinte erro do compilador, encontrado na linha que contém Guid g = new Guid("hello");

CS0576: O espaço para nome 'Microsoft.Sample' contém uma definição em conflito com o alias '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 de construtor correspondente. Posteriormente, o código cria uma instância do tipo Guid. Para criar essa instância, o compilador deve escolher entre as duas definições diferentes do Guid. Quando a diretiva using-alias é colocada fora do elemento do espaço para nome, o compilador escolhe a definição local de Guid definida no espaço para nome local e ignora completamente a diretiva using-alias definida fora do espaço para nome. Infelizmente, isso não é óbvio ao ler o código.

Quando a diretiva using-alias é posicionada no espaço para nome, no entanto, o compilador deve escolher entre dois tipos de Guid diferentes e conflitantes, ambos definidos no mesmo espaço para nome. Ambos os tipos fornecem um construtor correspondente. O compilador não pode tomar uma decisão; portanto, sinaliza o erro do compilador.

Colocar a diretiva using-alias fora do namespace é uma prática ruim, pois pode causar confusão em situações como essa, nas quais não é óbvio qual versão do tipo está realmente sendo usada. Isso pode levar a um bug que pode ser difícil de diagnosticar.

A colocação de diretivas using-alias no elemento namespace elimina isso como uma fonte de bugs.

  1. Vários namespaces

A colocação de vários elementos do espaço para nome em um único arquivo geralmente é uma má idéia, mas se e quando isso for feito, é uma boa idéia colocar todas as diretivas usando dentro de cada um dos elementos do espaço para nome, em vez de globalmente na parte superior do arquivo. Isso fará o escopo rigoroso dos espaços de nomes e também ajudará a evitar o tipo de comportamento descrito acima.

É importante observar que, quando o código foi escrito com o uso de diretivas colocadas fora do espaço para nome, deve-se tomar cuidado ao mover essas diretivas para dentro do espaço para nome, para garantir que isso não altere a semântica do código. Como explicado acima, a colocação de diretivas using-alias no elemento namespace permite que o compilador escolha entre tipos conflitantes de maneiras que não acontecerão quando as diretivas forem colocadas fora do namespace.

Como corrigir violações Para corrigir uma violação desta regra, mova todas as diretivas usando e alias usando o elemento de espaço para nome.

JaredCacurak
fonte
1
@ Jared - como observei na minha resposta, minha solução / solução preferida é ter apenas uma classe por arquivo. Eu acho que essa é uma convenção bastante comum.
benPearce 14/09/09
24
Na verdade, também é uma regra do StyleCop! SA1402: O documento AC # pode conter apenas uma única classe no nível raiz, a menos que todas as classes sejam parciais e sejam do mesmo tipo. Apresentar uma regra quebrando outra apenas pinga com o molho errado.
Tarefa
6
Promovido por ser a primeira resposta a realmente cobri-lo da perspectiva do StyleCop. Pessoalmente, gosto da sensação visual de usings fora do espaço para nome. Inner usings parece tão feio para mim. :)
Nawfal
2
Finalmente, uma boa resposta para a pergunta. E o comentário de benPearce é irrelevante ... isso não tem nada a ver com o número de classes no arquivo.
perfil completo de Jim Balter
35

Há um problema com a colocação de instruções using dentro do namespace quando você deseja usar aliases. O alias não se beneficia das usingdeclarações anteriores e deve ser totalmente qualificado.

Considerar:

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

    class MyClass
    {
    }
}

versus:

using System;

namespace MyNamespace
{
    using MyAlias = DateTime;

    class MyClass
    {
    }
}

Isso pode ser particularmente pronunciado se você tiver um alias longo, como o seguinte (que foi como eu encontrei o problema):

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

Com usinginstruções dentro do espaço para nome, torna-se repentinamente:

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

Feio.

Neo
fonte
1
Você classprecisa de um nome (identificador). Você não pode ter uma usingdiretiva dentro de uma classe, como indica. Ele deve estar no nível do espaço para nome, por exemplo, fora da extremidade externa namespaceou apenas dentro da parte interna namespace(mas não dentro de uma classe / interface / etc.).
Jeppe Stig Nielsen
@JeppeStigNielsen Thanks. Eu extraviei as usingdiretrizes por engano. Eu editei para como eu pretendia que fosse. Obrigado por apontar. O raciocínio ainda é o mesmo.
Neo
4

Como Jeppe Stig Nielsen disse , esse tópico já tem ótimas respostas, mas achei que essa sutileza bastante óbvia também merecia ser mencionada.

using as diretivas especificadas dentro dos namespaces podem gerar um código mais curto, pois não precisam ser totalmente qualificadas como quando são especificadas externamente.

O exemplo a seguir funciona porque os tipos Fooe Barestão ambos no mesmo espaço para nome global Outer,.

Suponha que 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 espaço para nome externo na usingdiretiva, para abreviar:

namespace Outer
{
    using Inner;

    class Bar
    {
        public Foo foo;
    }
}
biscoitos
fonte
8
É verdade que você "pode ​​omitir o espaço para nome externo", mas isso não significa que você deva. Para mim, esse é outro argumento sobre o motivo pelo qual o uso de diretivas (exceto apelidos como na resposta do @ Neo) deve sair do espaço para nome, para forçar nomes de espaço para nome totalmente qualificados.
Keith Robertson
4

Encontrei uma ruga (que não é abordada em outras respostas):

Suponha que você tenha esses espaços para nome:

  • Alguma outra coisa
  • Parent.Something.Other

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

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

Existe uma solução simples: adicione o global::prefixo " ": docs

namespace Parent
{
   using global::Something.Other;
   // etc
}
Hans Keing
fonte
2

As razões técnicas são discutidas nas respostas e acho que, no final, trata-se das preferências pessoais, já que a diferença não é tão grande e existem vantagens e desvantagens para as duas. O modelo padrão do Visual Studio para criar .csarquivos usa usingdiretivas fora dos espaços para nome, por exemplo

Pode-se ajustar o stylecop para verificar as usingdiretivas fora dos namespaces, adicionando um stylecop.jsonarquivo na raiz do arquivo do 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á-lo aos seus projetos como 'Arquivo de Link Existente' para compartilhar também a configuração em todos os seus projetos.

sotn
fonte
2

Outra sutileza que não acredito ter sido coberta por outras respostas é para quando você tem uma classe e um espaço para nome com o mesmo nome.

Quando você tiver a importação dentro do espaço para nome, ela encontrará a classe. Se a importação estiver fora do espaço para nome, a importação será ignorada e a classe e o espaço para nome deverão ser totalmente qualificados.

//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.
        }
    }
}
Ben Gardner
fonte
-8

É uma prática recomendada se aqueles que usam o padrão "ie" " referências " na sua solução de origem estiverem fora dos espaços para nome e aqueles que são "nova referência adicionada" for uma boa prática, se você o colocar dentro do espaço para nome. Isso é para distinguir quais referências estão sendo adicionadas.

Israel Ocbina
fonte
6
Não, na verdade isso é uma má ideia. Você não deve basear o local entre o escopo local e o global do uso de diretivas no fato de que elas foram adicionadas recentemente ou não. Em vez disso, é uma boa prática colocá-los em ordem alfabética, exceto as referências BCL, que devem ficar por cima.
Abel