14 Espaços de nomes
14.1 Generalidades
Os programas C# são organizados usando namespaces. Os namespaces são usados como um sistema de organização "interno" para um programa e como um sistema de organização "externo" — uma maneira de apresentar elementos do programa que são expostos a outros programas.
O uso de diretivas (§14.5) é fornecido 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_directives de 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 conjunto e módulo de destino. Montagens 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 final
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* '}'
;
Um namespace_declaration pode ocorrer como uma declaração de nível superior em um compilation_unit ou como uma declaração de membro dentro de 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 dentro de 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 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 importa os nomes de outros namespaces, tipos e membros, permitindo que eles sejam referenciados diretamente em vez de através de nomes qualificados. Os membros opcionais do namespace_member_declarations contribuem para o espaço de declaração do namespace. Note-se que todos os using_directivedevem aparecer antes de qualquer declaração de membro.
O qualified_identifier de um namespace_declaration pode ser um identificador único ou uma sequência de identificadores separados por tokens ".
". O último formulário permite que um programa defina um namespace aninhado sem aninhar lexicamente 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 final
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 final
14.4 Diretivas externas
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 âmbito de aplicação de um extern_alias_directive estende-se pelos using_directives, global_attributes e namespace_member_declarations dos seus compilation_unit ou namespace_body imediatamente contidos.
Dentro de 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 em tempo de compilação para o identificador ser a palavra global
.
O alias introduzido por um extern_alias_directive é muito semelhante ao alias introduzido por um using_alias_directive. Ver §14.5.2 para uma análise mais pormenorizada das extern_alias_directives e using_alias_directives.
alias
é uma palavra-chave contextual (§6.4.4) e só tem um significado especial quando se segue imediatamente à extern
palavra-chave num extern_alias_directive.
Um erro ocorre se um programa declara 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 final
14.5 Utilização de diretivas
14.5.1 Generalidades
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 dentro dos quais eles 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 do 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 ao longo do namespace_member_declarations de sua unidade de compilação ou corpo de namespace que contém imediatamente. O âmbito de aplicação de um using_directive especificamente não inclui os seus pares using_directives. Assim, os pares using_directives não se afetam mutuamente, e a ordem em que são escritos é insignificante. Em contraste, o escopo de um extern_alias_directive inclui os using_directives definidos 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 corpo de namespace imediatamente fechado.
using_alias_directive
: 'using' identifier '=' namespace_or_type_name ';'
;
Dentro de 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, há um alias paraN1.N2.A
, e, portanto,A
a classeN3.B
deriva da classeN1.N2.A
. O mesmo efeito pode ser obtido criando um aliasR
paraN1.N2
e, em seguida, fazendo referência aR.A
:namespace N3 { using R = N1.N2; class B : R.A {} }
Exemplo final
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. A classeN1.B
deriva da classeN2.A
. O mesmo efeito pode ser obtido criando um aliasA
paraN2.A
e, em seguida, fazendo referência aA
:namespace N1 { extern alias N2; using A = N2::A; class B : A {} }
Exemplo final
Um extern_alias_directive ou using_alias_directive disponibiliza um alias dentro de 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, em vez disso, afeta apenas a unidade de compilação ou o corpo de namespace em que 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 às 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 final
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 anexado. O identificador da diretiva dos pseudónimos deve ser único no espaço correspondente da declaração dos pseudónimos. 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 uso do alias nomeado
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
, uma vez que esses nomes são adicionados a espaços de declaração distintos. O primeiro é adicionado ao espaço de declaração global e o segundo é 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 adequadamente 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 de resultados em um erro, uma vezN3
que contém um membro nomeadoB
e o corpo doB
namespace que também declara um alias com nomeB
; 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 pseudónimoB
é essencialmente inútil. Ele não pode ser usado em um qualified_alias_member uma vez que apenas aliases de namespace podem ser usados em um qualified_alias_member eB
aliases um tipo.Exemplo final
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 na
R.A
declaração de causa um erro em tempo deB
compilação porqueR
se refere aN3.R
, nãoN1.N2
.Exemplo final
A ordem pela qual extern_alias_directives são escritos não tem qualquer significado. Da mesma forma, a ordem em que using_alias_directives são escritos não tem significado, mas todos os using_alias_directives virão depois de todos os extern_alias_directives na 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 corpo de namespace que contém imediatamente, mas pode ser afetada por extern_alias_directives na unidade de compilação ou corpo de namespace que contém imediatamente. Em outras palavras, o namespace_or_type_name de um using_alias_directive é resolvido como se a unidade de compilação ou o corpo de namespace imediatamente contendo 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 em tempo de compilação porque não é afetado pelo using_alias_directive anterior. A primeira using_alias_directive não resulta em erro, uma vez que o âmbito do alias externo X inclui o using_alias_directive.
Exemplo final
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 final
Embora cada parte de um tipo parcial (§15.2.7) seja declarada dentro do mesmo namespace, as partes geralmente são escritas dentro de 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) dentro de uma parte, apenas os extern_alias_directives e using_directives dos corpos de namespace e da unidade de compilação que encerram essa parte são considerados. Isto pode fazer com que o mesmo identificador tenha 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 final
O uso de aliases pode nomear um tipo construído fechado, mas não pode nomear uma declaração de tipo genérica não acoplada 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 final
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 de namespace imediatamente fechado, permitindo que o identificador de cada tipo seja usado sem qualificação.
using_namespace_directive
: 'using' namespace_name ';'
;
Dentro das 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, dentro das declarações de membro no
N3
namespace, os membros do tipo deN1.N2
estão diretamente disponíveis e, portanto, a classeN3.B
deriva da classeN1.N2.A
.Exemplo final
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 no
N1
, mas não os namespaces aninhados noN1
. Assim, a referência naN2.A
declaração de resultados em um erro em tempo deB
compilação porque nenhum membro nomeadoN2
está no escopo.Exemplo final
Ao contrário de um using_alias_directive, um using_namespace_directive pode importar tipos cujos identificadores já estão definidos dentro da unidade de compilação anexa ou do corpo do namespace. Na verdade, os nomes importados por um using_namespace_directive são ocultos por membros com nomes semelhantes na unidade de compilação ou no corpo do namespace que o inclui.
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 final
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 desambiguar a 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 porqueN3
importa ambos, referenciarA
emN3
é um erro em tempo de compilação. Nesta situação, o conflito pode ser resolvido através da qualificação de referências aA
, ou através da introdução de um using_alias_directive que escolhe um determinadoA
. Por exemplo:namespace N3 { using N1; using N2; using A = N1.A; class B : A {} // A means N1.A }
Exemplo final
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 porqueN2
importa ambos, aA
referência como um simple_name é ambígua e um erro em tempo de compilação.Exemplo final
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 escritos 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 corpo de namespace imediatamente adjacente, permitindo que o identificador de cada membro e tipo seja usado sem qualificação.
using_static_directive
: 'using' 'static' type_name ';'
;
Dentro das 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 dado 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
eM
membros doN1.A
.Exemplo final
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 noN1.A
, mas apenas como um método de extensão. Assim, a primeira referência aM
no corpo de resultados em um erro de tempo deB.N
compilação porque nenhum membro nomeadoM
está no escopo.Exemplo final
Um using_static_directive só importa membros e tipos declarados diretamente no tipo dado, 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 noN1.B
, mas não importa o métodoM
contido noN1.A
. Assim, a referência aM
no corpo de resultados em um erro de tempo deC.N
compilação porque nenhum membro nomeadoM
está no escopo. Os desenvolvedores devem adicionar uma segundausing static
diretiva para especificar que os métodos emN1.A
também devem ser importados.Exemplo final
As ambiguidades entre múltiplos using_namespace_directives e using_static_directives são discutidas no §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 corpo de namespace que 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
;
Um 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 numa 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 um namespace, classe ou declaração struct, o nome totalmente qualificado (§7.8.3) da declaração de tipo é S.N
, onde S
é o nome totalmente qualificado da declaração de namespace, classe ou struct que contém e N
é o nome não qualificado da declaração.
Um tipo declarado dentro de 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):
- 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
,internal
private protected
, ouprivate
acesso. O padrão éprivate
acesso. - Os tipos declarados em structs podem ter
public
,internal
, ouprivate
acesso. O padrão éprivate
acesso.
14.8 Membro qualificado do alias
14.8.1 Generalidades
O qualificador ::
de alias de namespace torna possível 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 esquerdo e direito. Ao contrário do qualificador regular .
, o identificador esquerdo do qualificador é pesquisado ::
apenas como um externo ou usando um alias.
Um qualified_alias_member fornece acesso explícito ao namespace global e ao externo 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, referidos como os identificadores do lado esquerdo e do lado direito, separados pelo ::
token e, opcionalmente, seguidos por um type_argument_list. Quando o identificador da esquerda é global, o namespace global é pesquisado pelo identificador da direita. Para qualquer outro identificador do lado esquerdo, esse identificador é procurado como externo ou usando alias (§14.4 e §14.5.2). Um erro em tempo de compilação ocorre 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 do lado direito.
Um qualified_alias_member tem uma de 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 esta notação, o significado de um qualified_alias_member é determinado da seguinte forma:
- Se
N
for o identificadorglobal
, então o namespace global é pesquisado porI
:- Se o namespace global contiver um namespace chamado
I
ee
for zero, o qualified_alias_member se refere 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 refere a esse tipo. - Caso contrário, se o namespace global contiver um tipo nomeado
I
que tenhae
parâmetros de tipo, o qualified_alias_member se refere a esse tipo construído com os argumentos de tipo fornecidos. - Caso contrário, o qualified_alias_member é indefinido e ocorre 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) contendo imediatamente o qualified_alias_member (se houver), continuando com cada declaração de namespace anexando (se houver) e terminando com a unidade de compilação que contém o qualified_alias_member, as seguintes etapas sã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 associe 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 a
N
contém um namespace chamadoI
ee
é zero, então o qualified_alias_member refere-se 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 refere a esse tipo. - Caso contrário, se o namespace associado contiver
N
um tipo nomeadoI
que tenhae
parâmetros de tipo, o qualified_alias_member se refere a esse tipo construído com os argumentos de tipo fornecidos. - Caso contrário, o qualified_alias_member é indefinido e ocorre um erro em tempo de compilação.
- Se o namespace associado a
- Caso contrário, o qualified_alias_member é indefinido e ocorre 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 final
Nota: O identificador
global
tem um significado especial apenas quando utilizado como identificador esquerdo de um qualified_alias_name. Não é uma palavra-chave e não é, em si mesmo, 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 }
Usar
global.A
causa um erro em tempo de compilação, uma vez que não há nenhuma entidade nomeadaglobal
no escopo. Se alguma entidade denominada global estivesse no escopo, entãoglobal
emglobal.A
teria resolvido para essa entidade.Usar
global
como identificador esquerdo de um qualified_alias_member sempre causa uma pesquisa noglobal
namespace, mesmo que haja um alias de uso 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 paraMyGlobalTypes.A
eglobal::A
resolve para classeA
no namespace global.Nota final
14.8.2 Singularidade dos pseudónimos
Cada unidade de compilação e corpo de namespace tem um espaço de declaração separado para aliases externos e usando aliases. Assim, enquanto o nome de um alias externo ou usando alias deve ser único dentro do conjunto de aliases externos e usando aliases declarados na unidade de compilação imediatamente contendo ou corpo de namespace, 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 de namespace porque a classeA
e o aliasA
de uso estão no escopo. Por esse motivo, o uso de no nomeA.Stream
qualificado é ambíguo e causa um erro em tempo deA
compilação. No entanto, o uso de com o::
qualificador não é um erro porqueA
é pesquisado apenas como um alias deA
namespace.Exemplo final
ECMA C# draft specification