14 Espaços nominais
14.1 Geral
Os programas C# são organizados usando namespaces. Os namespaces são usados como um sistema organizacional "interno" para um programa e como um sistema organizacional "externo" - uma maneira de apresentar elementos do programa que são expostos a outros programas.
As diretivas de uso (§14.5) são fornecidas para facilitar o uso de namespaces.
14.2 Unidades de compilação
Um compilation_unit consiste em zero ou mais extern_alias_directives seguido por zero ou mais using_directives seguido por zero ou um global_attributes seguido por zero ou mais namespace_member_declarations. O compilation_unit define a estrutura geral da entrada.
compilation_unit
: extern_alias_directive* using_directive* global_attributes?
namespace_member_declaration*
;
Um programa C# consiste em uma ou mais unidades de compilação. Quando um programa C# é compilado, todas as unidades de compilação são processadas juntas. Assim, as unidades de compilação podem depender umas das outras, possivelmente de forma circular.
Os extern_alias_directives de uma unidade de compilação afetam os using_directives, global_attributes e namespace_member_declarations dessa unidade de compilação, mas não têm efeito sobre outras unidades de compilação.
Os using_directivede uma unidade de compilação afetam os global_attributes e namespace_member_declarations dessa unidade de compilação, mas não têm efeito sobre outras unidades de compilação.
O global_attributes (§22.3) de uma unidade de compilação permite a especificação de atributos para o assembly e o módulo de destino. Assemblies e módulos atuam como contêineres físicos para tipos. Um conjunto pode consistir em vários módulos fisicamente separados.
Os namespace_member_declarations de cada unidade de compilação de um programa contribuem com membros para um único espaço de declaração chamado namespace global.
Exemplo:
// File A.cs: class A {} // File B.cs: class B {}
As duas unidades de compilação contribuem para o namespace global único, neste caso, declarando duas classes com os nomes
A
totalmente qualificados eB
. Como as duas unidades de compilação contribuem para o mesmo espaço de declaração, teria sido um erro se cada uma contivesse uma declaração de um membro com o mesmo nome.exemplo de fim
14.3 Declarações de namespace
Um namespace_declaration consiste no namespace da palavra-chave, seguido por um nome e corpo do namespace, opcionalmente seguido por um ponto-e-vírgula.
namespace_declaration
: 'namespace' qualified_identifier namespace_body ';'?
;
qualified_identifier
: identifier ('.' identifier)*
;
namespace_body
: '{' extern_alias_directive* using_directive*
namespace_member_declaration* '}'
;
Uma namespace_declaration pode ocorrer como uma declaração de nível superior em um compilation_unit ou como uma declaração de membro em outro namespace_declaration. Quando um namespace_declaration ocorre como uma declaração de nível superior em um compilation_unit, o namespace se torna um membro do namespace global. Quando um namespace_declaration ocorre em outro namespace_declaration, o namespace interno se torna um membro do namespace externo. Em ambos os casos, o nome de um namespace deve ser exclusivo dentro do namespace que o contém.
Os namespaces são implicitamente public
e a declaração de um namespace não pode incluir nenhum modificador de acesso.
Dentro de um namespace_body, o using_directiveopcional s importam os nomes de outros namespaces, tipos e membros, permitindo que eles sejam referenciados diretamente em vez de por meio de nomes qualificados. Os membros de contribuição do namespace_member_declarationopcional para o espaço de declaração do namespace. Observe que todos os using_directive devem aparecer antes dequalquer declaração de membro.
O qualified_identifier de um namespace_declaration pode ser um único identificador ou uma sequência de identificadores separados por tokens ".
". A última forma permite que um programa defina um namespace aninhado sem aninhar lexicalmente várias declarações de namespace.
Exemplo:
namespace N1.N2 { class A {} class B {} }
é semanticamente equivalente a
namespace N1 { namespace N2 { class A {} class B {} } }
exemplo de fim
Os namespaces são abertos e duas declarações de namespace com o mesmo nome totalmente qualificado (§7.8.2) contribuem para o mesmo espaço de declaração (§7.3).
Exemplo: no código a seguir
namespace N1.N2 { class A {} } namespace N1.N2 { class B {} }
As duas declarações de namespace acima contribuem para o mesmo espaço de declaração, neste caso, declarando duas classes com os nomes
N1.N2.A
totalmente qualificados eN1.N2.B
. Como as duas declarações contribuem para o mesmo espaço de declaração, teria sido um erro se cada uma contivesse uma declaração de um membro com o mesmo nome.exemplo de fim
14.4 Diretivas de alias externos
Um extern_alias_directive introduz um identificador que serve como um alias para um namespace. A especificação do namespace com alias é externa ao código-fonte do programa e também se aplica a namespaces aninhados do namespace com alias.
extern_alias_directive
: 'extern' 'alias' identifier ';'
;
O escopo de um extern_alias_directive se estende ao longo dos using_directive, global_attributes e namespace_member_declarations de seu compilation_unit ou namespace_body imediatamente contido.
Em uma unidade de compilação ou corpo de namespace que contém um extern_alias_directive, o identificador introduzido pelo extern_alias_directive pode ser usado para fazer referência ao namespace com alias. É um erro de tempo de compilação que o identificador seja a palavra global
.
O alias introduzido por um extern_alias_directive é muito semelhante ao alias introduzido por um using_alias_directive. Consulte §14.5.2 para uma discussão mais detalhada sobre extern_alias_directivee using_alias_directives.
alias
é uma palavra-chave contextual (§6.4.4) e só tem um significado especial quando segue imediatamente a extern
palavra-chave em um extern_alias_directive.
Ocorrerá um erro se um programa declarar um alias externo para o qual nenhuma definição externa é fornecida.
Exemplo: o programa a seguir declara e usa dois aliases
X
externos eY
, cada um dos quais representa a raiz de uma hierarquia de namespace distinta:extern alias X; extern alias Y; class Test { X::N.A a; X::N.B b1; Y::N.B b2; Y::N.C c; }
O programa declara a existência dos aliases
X
externos eY
, mas as definições reais dos aliases são externas ao programa. As classes com nomesN.B
idênticos agora podem ser referenciadas comoX.N.B
eY.N.B
, ou, usando o qualificadorX::N.B
de alias de namespace eY::N.B
. exemplo de fim
14.5 Usando diretivas
14.5.1 Geral
O uso de diretivas facilita o uso de namespaces e tipos definidos em outros namespaces. O uso de diretivas afeta o processo de resolução de nomes de namespace_or_type_name s (§7.8) e simple_names (§12.8.4), mas, ao contrário das declarações, using_directives não contribuem com novos membros para os espaços de declaração subjacentes das unidades de compilação ou namespaces nosquais são usados.
using_directive
: using_alias_directive
| using_namespace_directive
| using_static_directive
;
Um using_alias_directive (§14.5.2) introduz um alias para um namespace ou tipo.
Um using_namespace_directive (§14.5.3) importa os membros de tipo de um namespace.
Um using_static_directive (§14.5.4) importa os tipos aninhados e os membros estáticos de um tipo.
O escopo de um using_directive se estende pela namespace_member_declarations de sua unidade de compilação ou corpo de namespace que contém imediatamente. O escopo de um using_directive especificamente não inclui seus using_directivepares. Assim, os using_directivepares não afetam uns aos outros e a ordem em que são escritos é insignificante. Por outro lado, o escopo de um extern_alias_directive inclui os using_directivedefinidos na mesma unidade de compilação ou corpo de namespace.
14.5.2 Usando diretivas de alias
Um using_alias_directive introduz um identificador que serve como um alias para um namespace ou tipo dentro da unidade de compilação ou do corpo do namespace imediatamente delimitador.
using_alias_directive
: 'using' identifier '=' namespace_or_type_name ';'
;
Em atributos globais e declarações de membro em uma unidade de compilação ou corpo de namespace que contém um using_alias_directive, o identificador introduzido pelo using_alias_directive pode ser usado para fazer referência ao namespace ou tipo fornecido.
Exemplo:
namespace N1.N2 { class A {} } namespace N3 { using A = N1.N2.A; class B: A {} }
Acima, dentro das declarações de membro no
N3
namespace,A
há um alias paraN1.N2.A
, e, portanto, classN3.B
deriva de classN1.N2.A
. O mesmo efeito pode ser obtido criando um aliasR
paraN1.N2
e, em seguida, referenciandoR.A
:namespace N3 { using R = N1.N2; class B : R.A {} }
exemplo de fim
Ao usar diretivas, atributos globais e declarações de membro em uma unidade de compilação ou corpo de namespace que contém um extern_alias_directive, o identificador introduzido pelo extern_alias_directive pode ser usado para fazer referência ao namespace associado.
Exemplo: Por exemplo:
namespace N1 { extern alias N2; class B : N2::A {} }
Acima, dentro das declarações de membro no
N1
namespace,N2
há um alias para algum namespace cuja definição é externa ao código-fonte do programa. ClasseN1.B
deriva de classeN2.A
. O mesmo efeito pode ser obtido criando um aliasA
paraN2.A
e, em seguida, referenciandoA
:namespace N1 { extern alias N2; using A = N2::A; class B : A {} }
exemplo de fim
Um extern_alias_directive ou using_alias_directive disponibiliza um alias em uma unidade de compilação ou corpo de namespace específico, mas não contribui com novos membros para o espaço de declaração subjacente. Em outras palavras, uma diretiva de alias não é transitiva, mas afeta apenas a unidade de compilação ou o corpo do namespace no qual ela ocorre.
Exemplo: no código a seguir
namespace N3 { extern alias R1; using R2 = N1.N2; } namespace N3 { class B : R1::A, R2.I {} // Error, R1 and R2 unknown }
Os escopos das diretivas de alias que introduzem
R1
eR2
se estendem apenas a declarações de membro no corpo do namespace no qual elas estão contidas, portantoR1
, eR2
são desconhecidos na segunda declaração de namespace. No entanto, colocar as diretivas de alias na unidade de compilação que contém faz com que o alias fique disponível em ambas as declarações de namespace:extern alias R1; using R2 = N1.N2; namespace N3 { class B : R1::A, R2.I {} } namespace N3 { class C : R1::A, R2.I {} }
exemplo de fim
Cada extern_alias_directive ou using_alias_directive em um compilation_unit ou namespace_body contribui com um nome para o espaço de declaração de alias (§7.3) do compilation_unit ou namespace_body imediatamente delimitador. O identificador da diretiva alias deve ser exclusivo dentro do espaço de declaração de alias correspondente. O identificador de alias não precisa ser exclusivo dentro do espaço de declaração global ou do espaço de declaração do namespace correspondente.
Exemplo:
extern alias X; extern alias Y; using X = N1.N2; // Error: alias X already exists class Y {} // Ok
O alias using named
X
causa um erro, pois já existe um alias nomeadoX
na mesma unidade de compilação. A classe nomeadaY
não entra em conflito com o alias externo nomeadoY
, pois esses nomes são adicionados a espaços de declaração distintos. O primeiro é adicionado ao espaço de declaração global e o último é adicionado ao espaço de declaração de alias para esta unidade de compilação.Quando um nome de alias corresponde ao nome de um membro de um namespace, o uso de qualquer um deles deve ser devidamente qualificado:
namespace N1.N2 { class B {} } namespace N3 { class A {} class B : A {} } namespace N3 { using A = N1.N2; using B = N1.N2.B; class W : B {} // Error: B is ambiguous class X : A.B {} // Error: A is ambiguous class Y : A::B {} // Ok: uses N1.N2.B class Z : N3.B {} // Ok: uses N3.B }
No segundo corpo do namespace para
N3
, o uso não qualificado deB
resulta em um erro, sinceN3
contém um membro chamadoB
e o corpo do namespace que também declara um alias com nameB
; da mesma forma paraA
. A classeN3.B
pode ser referenciada comoN3.B
ouglobal::N3.B
. O aliasA
pode ser usado em um membro qualificado do alias (§14.8), comoA::B
. O aliasB
é essencialmente inútil. Ele não pode ser usado em um qualified_alias_member pois apenas aliases de namespace podem ser usados em um qualified_alias_member eB
aliases em um tipo.exemplo de fim
Assim como os membros regulares, os nomes introduzidos por alias_directives são ocultos por membros com nomes semelhantes em escopos aninhados.
Exemplo: no código a seguir
using R = N1.N2; namespace N3 { class R {} class B: R.A {} // Error, R has no member A }
A referência a
R.A
na declaração deB
causa um erro de tempo de compilação porqueR
se refere aN3.R
, nãoN1.N2
.exemplo de fim
A ordem em que extern_alias_directivesão escritas não tem significado. Da mesma forma, a ordem em que using_alias_directives são escritas não tem significado, mas todas as using_alias_directives virão depois de todas as extern_alias_directivena mesma unidade de compilação ou corpo de namespace. A resolução do namespace_or_type_name referenciado por um using_alias_directive não é afetada pelo using_alias_directive em si ou por outros using_directives na unidade de compilação ou no corpo do namespace que contém imediatamente, mas pode ser afetada por extern_alias_directives na unidade de compilação ou no corpo do namespace que contém imediatamente. Em outras palavras, a namespace_or_type_name de um using_alias_directive é resolvida como se a unidade de compilação ou o corpo do namespace que contém imediatamente não tivesse using_directives, mas tivesse o conjunto correto de extern_alias_directives.
Exemplo: no código a seguir
namespace N1.N2 {} namespace N3 { extern alias X; using R1 = X::N; // OK using R2 = N1; // OK using R3 = N1.N2; // OK using R4 = R2.N2; // Error, R2 unknown }
O último using_alias_directive resulta em um erro de tempo de compilação porque não é afetado pelo using_alias_directive anterior. A primeira using_alias_directive não resulta em um erro, pois o escopo do alias externo X inclui o using_alias_directive.
exemplo de fim
Um using_alias_directive pode criar um alias para qualquer namespace ou tipo, incluindo o namespace no qual ele aparece e qualquer namespace ou tipo aninhado nesse namespace.
Acessar um namespace ou tipo por meio de um alias produz exatamente o mesmo resultado que acessar esse namespace ou tipo por meio de seu nome declarado.
Exemplo: Dado
namespace N1.N2 { class A {} } namespace N3 { using R1 = N1; using R2 = N1.N2; class B { N1.N2.A a; // refers to N1.N2.A R1.N2.A b; // refers to N1.N2.A R2.A c; // refers to N1.N2.A } }
Os nomes
N1.N2.A
,R1.N2.A
, eR2.A
são equivalentes e todos se referem à declaração de classe cujo nome totalmente qualificado éN1.N2.A
.exemplo de fim
Embora cada parte de um tipo parcial (§15.2.7) seja declarada no mesmo namespace, as partes normalmente são gravadas em declarações de namespace diferentes. Assim, diferentes extern_alias_directives e using_directives podem estar presentes para cada parte. Ao interpretar nomes simples (§12.8.4) em uma parte, apenas os extern_alias_directivee using_directives dos corpos do namespace e da unidade de compilação que envolve essa parte são considerados. Isso pode resultar no mesmo identificador com significados diferentes em partes diferentes.
Exemplo:
namespace N { using List = System.Collections.ArrayList; partial class A { List x; // x has type System.Collections.ArrayList } } namespace N { using List = Widgets.LinkedList; partial class A { List y; // y has type Widgets.LinkedList } }
exemplo de fim
O uso de aliases pode nomear um tipo construído fechado, mas não pode nomear uma declaração de tipo genérico não associada sem fornecer argumentos de tipo.
Exemplo:
namespace N1 { class A<T> { class B {} } } namespace N2 { using W = N1.A; // Error, cannot name unbound generic type using X = N1.A.B; // Error, cannot name unbound generic type using Y = N1.A<int>; // Ok, can name closed constructed type using Z<T> = N1.A<T>; // Error, using alias cannot have type parameters }
exemplo de fim
14.5.3 Usando diretivas de namespace
Um using_namespace_directive importa os tipos contidos em um namespace para a unidade de compilação ou corpo do namespace imediatamente delimitador, permitindo que o identificador de cada tipo seja usado sem qualificação.
using_namespace_directive
: 'using' namespace_name ';'
;
Nas declarações de membro em uma unidade de compilação ou corpo de namespace que contém um using_namespace_directive, os tipos contidos no namespace fornecido podem ser referenciados diretamente.
Exemplo:
namespace N1.N2 { class A {} } namespace N3 { using N1.N2; class B : A {} }
Acima, nas declarações de membro no
N3
namespace, os membros do tipo deN1.N2
estão diretamente disponíveis e, portanto, classN3.B
deriva de classN1.N2.A
.exemplo de fim
Um using_namespace_directive importa os tipos contidos no namespace fornecido, mas especificamente não importa namespaces aninhados.
Exemplo: no código a seguir
namespace N1.N2 { class A {} } namespace N3 { using N1; class B : N2.A {} // Error, N2 unknown }
O using_namespace_directive importa os tipos contidos em
N1
, mas não os namespaces aninhados emN1
. Assim, a referência naN2.A
declaração de resulta em um erro deB
tempo de compilação porque nenhum membro nomeadoN2
está no escopo.exemplo de fim
Ao contrário de um using_alias_directive, um using_namespace_directive pode importar tipos cujos identificadores já estão definidos na unidade de compilação ou no corpo do namespace delimitador. Na verdade, os nomes importados por um using_namespace_directive são ocultados por membros com nomes semelhantes na unidade de compilação ou no corpo do namespace delimitador.
Exemplo:
namespace N1.N2 { class A {} class B {} } namespace N3 { using N1.N2; class A {} }
Aqui, dentro das declarações de membro no
N3
namespace,A
refere-se aN3.A
em vez deN1.N2.A
.exemplo de fim
Como os nomes podem ser ambíguos quando mais de um namespace importado introduz o mesmo nome de tipo, um using_alias_directive é útil para remover a ambiguidade da referência.
Exemplo: no código a seguir
namespace N1 { class A {} } namespace N2 { class A {} } namespace N3 { using N1; using N2; class B : A {} // Error, A is ambiguous }
ambos
N1
eN2
contêm um membroA
e, comoN3
importa ambos, aA
referência éN3
um erro em tempo de compilação. Nessa situação, o conflito pode ser resolvido por meio da qualificação de referências aA
, ou pela introdução de um using_alias_directive que escolhe um .A
Por exemplo:namespace N3 { using N1; using N2; using A = N1.A; class B : A {} // A means N1.A }
exemplo de fim
Além disso, quando mais de um namespace ou tipo importado por using_namespace_directives ou using_static_directives na mesma unidade de compilação ou corpo de namespace contêm tipos ou membros com o mesmo nome, as referências a esse nome como um simple_name são consideradas ambíguas.
Exemplo:
namespace N1 { class A {} } class C { public static int A; } namespace N2 { using N1; using static C; class B { void M() { A a = new A(); // Ok, A is unambiguous as a type-name A.Equals(2); // Error, A is ambiguous as a simple-name } } }
N1
contém um membroA
de tipo eC
contém um campoA
estático e, comoN2
importa ambos, aA
referência como um simple_name é ambígua e um erro em tempo de compilação.exemplo de fim
Como um using_alias_directive, um using_namespace_directive não contribui com novos membros para o espaço de declaração subjacente da unidade de compilação ou namespace, mas, em vez disso, afeta apenas a unidade de compilação ou o corpo do namespace no qual ele aparece.
O namespace_name referenciado por um using_namespace_directive é resolvido da mesma forma que o namespace_or_type_name referenciado por um using_alias_directive. Assim, using_namespace_directives na mesma unidade de compilação ou corpo de namespace não afetam uns aos outros e podem ser gravados em qualquer ordem.
14.5.4 Usando diretivas estáticas
Um using_static_directive importa os tipos aninhados e os membros estáticos contidos diretamente em uma declaração de tipo para a unidade de compilação ou o corpo do namespace imediatamente delimitador, permitindo que o identificador de cada membro e tipo seja usado sem qualificação.
using_static_directive
: 'using' 'static' type_name ';'
;
Dentro de declarações de membro em uma unidade de compilação ou corpo de namespace que contém um using_static_directive, os tipos aninhados acessíveis e membros estáticos (exceto métodos de extensão) contidos diretamente na declaração do tipo fornecido podem ser referenciados diretamente.
Exemplo:
namespace N1 { class A { public class B {} public static B M() => new B(); } } namespace N2 { using static N1.A; class C { void N() { B b = M(); } } }
No código anterior, dentro das declarações de membro no
N2
namespace, os membros estáticos e os tipos aninhados de estão diretamente disponíveis e, portanto, o métodoN
é capaz deN1.A
fazer referência aosB
membros eM
deN1.A
.exemplo de fim
Um using_static_directive especificamente não importa métodos de extensão diretamente como métodos estáticos, mas os disponibiliza para invocação de método de extensão (§12.8.9.3).
Exemplo:
namespace N1 { static class A { public static void M(this string s){} } } namespace N2 { using static N1.A; class B { void N() { M("A"); // Error, M unknown "B".M(); // Ok, M known as extension method N1.A.M("C"); // Ok, fully qualified } } }
O using_static_directive importa o método
M
de extensão contido emN1.A
, mas apenas como um método de extensão. Assim, a primeira referência aM
no corpo de resulta em um erro deB.N
tempo de compilação porque nenhum membro nomeadoM
está no escopo.exemplo de fim
Um using_static_directive importa apenas membros e tipos declarados diretamente no tipo fornecido, não membros e tipos declarados em classes base.
Exemplo:
namespace N1 { class A { public static void M(string s){} } class B : A { public static void M2(string s){} } } namespace N2 { using static N1.B; class C { void N() { M2("B"); // OK, calls B.M2 M("C"); // Error. M unknown } } }
O using_static_directive importa o método
M2
contido emN1.B
, mas não importa o métodoM
contido emN1.A
. Assim, a referência aM
no corpo de resulta em um erro deC.N
tempo de compilação porque nenhum membro nomeadoM
está no escopo. Os desenvolvedores devem adicionar uma segundausing static
diretiva para especificar que os métodos tambémN1.A
devem ser importados.exemplo de fim
As ambiguidades entre vários using_namespace_directives e using_static_directives são discutidas em §14.5.3.
14.6 Declarações de membro do namespace
Um namespace_member_declaration é um namespace_declaration (§14.3) ou um type_declaration (§14.7).
namespace_member_declaration
: namespace_declaration
| type_declaration
;
Uma unidade de compilação ou um corpo de namespace pode conter namespace_member_declarations, e essas declarações contribuem com novos membros para o espaço de declaração subjacente da unidade de compilação ou do corpo do namespace que o contém.
14.7 Declarações de tipo
Um type_declaration é um class_declaration (§15.2), um struct_declaration (§16.2), um interface_declaration (§18.2), um enum_declaration (§19.2) ou um delegate_declaration (§20.2).
type_declaration
: class_declaration
| struct_declaration
| interface_declaration
| enum_declaration
| delegate_declaration
;
Uma type_declaration pode ocorrer como uma declaração de nível superior em uma unidade de compilação ou como uma declaração de membro dentro de um namespace, classe ou struct.
Quando uma declaração de tipo para um tipo T
ocorre como uma declaração de nível superior em uma unidade de compilação, o nome totalmente qualificado (§7.8.2) da declaração de tipo é o mesmo que o nome não qualificado da declaração (§7.8.2). Quando uma declaração de tipo para um tipo T
ocorre dentro de uma declaração de namespace, classe ou struct, o nome totalmente qualificado (§7.8.3) do tipo declarationis S.N
, onde S
é o nome totalmente qualificado da declaração de namespace, classe ou struct que o contém e N
é o nome não qualificado da declaração.
Um tipo declarado em uma classe ou struct é chamado de tipo aninhado (§15.3.9).
Os modificadores de acesso permitidos e o acesso padrão para uma declaração de tipo dependem do contexto em que a declaração ocorre (§7.5.2):
- Os tipos declarados em unidades de compilação ou namespaces podem ter
public
ouinternal
acessar. O padrão éinternal
acesso. - Os tipos declarados em classes podem ter
public
,protected internal
,protected
,private protected
,internal
, ouprivate
access. O padrão éprivate
acesso. - Os tipos declarados em structs podem ter
public
,internal
, ouprivate
access. O padrão éprivate
acesso.
14.8 Membro pseudônimo qualificado
14.8.1 Geral
O qualificador ::
de alias de namespace possibilita garantir que as pesquisas de nome de tipo não sejam afetadas pela introdução de novos tipos e membros. O qualificador de alias de namespace sempre aparece entre dois identificadores chamados de identificadores à esquerda e à direita. Ao contrário do qualificador regular .
, o identificador à esquerda do qualificador é procurado ::
apenas como um extern ou usando alias.
Um qualified_alias_member fornece acesso explícito ao namespace global e a extern ou usando aliases que são potencialmente ocultos por outras entidades.
qualified_alias_member
: identifier '::' identifier type_argument_list?
;
Um qualified_alias_member pode ser usado como um namespace_or_type_name (§7.8) ou como o operando esquerdo em um member_access (§12.8.7).
Um qualified_alias_member consiste em dois identificadores, chamados de identificadores à esquerda e à direita, separados pelo ::
token e, opcionalmente, seguidos por um type_argument_list. Quando o identificador à esquerda é global, o namespace global é pesquisado para o identificador à direita. Para qualquer outro identificador à esquerda, esse identificador é procurado como um alias externo ou de uso (§14.4 e §14.5.2). Um erro de tempo de compilação ocorrerá se não houver esse alias ou se o alias fizer referência a um tipo. Se o alias fizer referência a um namespace, esse namespace será pesquisado pelo identificador à direita.
Um qualified_alias_member tem uma das duas formas:
N::I<A₁, ..., Aₑ>
, ondeN
eI
representam identificadores e<A₁, ..., Aₑ>
é uma lista de argumentos de tipo.e
( é sempre pelo menos um.)N::I
, ondeN
eI
representam identificadores. (Neste caso,e
é considerado zero.)
Usando essa notação, o significado de um qualified_alias_member é determinado da seguinte forma:
- Se
N
for o identificadorglobal
, o namespace global será pesquisadoI
:- Se o namespace global contiver um namespace chamado
I
ee
for zero, o qualified_alias_member se referirá a esse namespace. - Caso contrário, se o namespace global contiver um tipo não genérico chamado
I
ee
for zero, o qualified_alias_member se referirá a esse tipo. - Caso contrário, se o namespace global contiver um tipo chamado
I
que tenhae
parâmetros de tipo, o qualified_alias_member se referirá a esse tipo construído com os argumentos de tipo fornecidos. - Caso contrário, o qualified_alias_member será indefinido e ocorrerá um erro em tempo de compilação.
- Se o namespace global contiver um namespace chamado
- Caso contrário, começando com a declaração de namespace (§14.3) que contém imediatamente o qualified_alias_member (se houver), continuando com cada declaração de namespace delimitadora (se houver) e terminando com a unidade de compilação que contém o qualified_alias_member, as seguintes etapas serão avaliadas até que uma entidade seja localizada:
- Se a declaração de namespace ou a unidade de compilação contiver um using_alias_directive que associa N a um tipo, o qualified_alias_member será indefinido e ocorrerá um erro em tempo de compilação.
- Caso contrário, se a declaração de namespace ou a unidade de compilação contiver um extern_alias_directive ou using_alias_directive associado a
N
um namespace, então:- Se o namespace associado contiver
N
um namespace chamadoI
ee
for zero, o qualified_alias_member se referirá a esse namespace. - Caso contrário, se o namespace associado contiver
N
um tipo não genérico chamadoI
ee
for zero, o qualified_alias_member se referirá a esse tipo. - Caso contrário, se o namespace associado contiver
N
um tipo chamadoI
que tenhae
parâmetros de tipo, o qualified_alias_member se referirá a esse tipo construído com os argumentos de tipo fornecidos. - Caso contrário, o qualified_alias_member será indefinido e ocorrerá um erro em tempo de compilação.
- Se o namespace associado contiver
- Caso contrário, o qualified_alias_member será indefinido e ocorrerá um erro em tempo de compilação.
Exemplo: No código:
using S = System.Net.Sockets; class A { public static int x; } class C { public void F(int A, object S) { // Use global::A.x instead of A.x global::A.x += A; // Use S::Socket instead of S.Socket S::Socket s = S as S::Socket; } }
A classe
A
é referenciada comglobal::A
e o tipoSystem.Net.Sockets.Socket
é referenciado comS::Socket
. UsarA.x
eS.Socket
, em vez disso, teria causado erros em tempo de compilação porqueA
eS
teria resolvido para os parâmetros.exemplo de fim
Nota: O identificador
global
tem um significado especial apenas quando usado como o identificador à esquerda de um qualified_alias_name. Não é uma palavra-chave e não é em si um pseudônimo; é uma palavra-chave contextual (§6.4.4). No código:class A { } class C { global.A x; // Error: global is not defined global::A y; // Valid: References A in the global namespace }
using
global.A
causa um erro de tempo de compilação, pois não há nenhuma entidade nomeadaglobal
no escopo. Se alguma entidade chamada global estivesse no escopo, entãoglobal
elaglobal.A
teria resolvido para essa entidade.Usar
global
como o identificador à esquerda de um qualified_alias_member sempre causa uma pesquisa noglobal
namespace, mesmo que haja um alias using chamadoglobal
. No código:using global = MyGlobalTypes; class A { } class C { global.A x; // Valid: References MyGlobalTypes.A global::A y; // Valid: References A in the global namespace }
global.A
resolve eMyGlobalTypes.A
global::A
resolve para a classeA
no namespace global.nota final
14.8.2 Unicidade de aliases
Cada unidade de compilação e corpo de namespace tem um espaço de declaração separado para aliases externos e aliases de uso. Assim, embora o nome de um alias externo ou alias using deva ser exclusivo dentro do conjunto de aliases externos e usando aliases declarados na unidade de compilação ou no corpo do namespace que contém imediatamente, um alias pode ter o mesmo nome que um tipo ou namespace, desde que seja usado apenas com o ::
qualificador.
Exemplo: No seguinte:
namespace N { public class A {} public class B {} } namespace N { using A = System.IO; class X { A.Stream s1; // Error, A is ambiguous A::Stream s2; // Ok } }
O nome
A
tem dois significados possíveis no segundo corpo do namespace porque a classeA
e o aliasA
using estão no escopo. Por esse motivo, o uso deA
no nomeA.Stream
qualificado é ambíguo e causa um erro em tempo de compilação. No entanto, o uso de com o qualificador não é um erro porqueA
é pesquisadoA
::
apenas como um alias de namespace.exemplo de fim
ECMA C# draft specification