Compartilhar via


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 e B. 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 e N1.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 e Y, 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 e Y, mas as definições reais dos aliases são externas ao programa. As classes com nomes N.B idênticos agora podem ser referenciadas como X.N.B e Y.N.B, ou, usando o qualificador X::N.B de alias de namespace e Y::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 para N1.N2.A, e, portanto, class N3.B deriva de class N1.N2.A. O mesmo efeito pode ser obtido criando um alias R para N1.N2 e, em seguida, referenciando R.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. Classe N1.B deriva de classe N2.A. O mesmo efeito pode ser obtido criando um alias A para N2.A e, em seguida, referenciando A:

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 e R2 se estendem apenas a declarações de membro no corpo do namespace no qual elas estão contidas, portanto R1 , e R2 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 nomeado X na mesma unidade de compilação. A classe nomeada Y não entra em conflito com o alias externo nomeado Y , 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 de B resulta em um erro, since N3 contém um membro chamado B e o corpo do namespace que também declara um alias com name B; da mesma forma para A. A classe N3.B pode ser referenciada como N3.B ou global::N3.B. O alias A pode ser usado em um membro qualificado do alias (§14.8), como A::B. O alias B é 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 e B 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 de B causa um erro de tempo de compilação porque R se refere a N3.R, não N1.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, e R2.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 de N1.N2 estão diretamente disponíveis e, portanto, class N3.B deriva de class N1.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 em N1. Assim, a referência na N2.A declaração de resulta em um erro de B tempo de compilação porque nenhum membro nomeado N2 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 a N3.A em vez de N1.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 e N2 contêm um membro Ae, como N3 importa ambos, a A 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 a A, 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 membro Ade tipo e C contém um campo Aestático e, como N2 importa ambos, a A 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étodo N é capaz de N1.A fazer referência aos B membros e M de N1.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 em N1.A, mas apenas como um método de extensão. Assim, a primeira referência a M no corpo de resulta em um erro de B.N tempo de compilação porque nenhum membro nomeado M 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 em N1.B, mas não importa o método M contido em N1.A. Assim, a referência a M no corpo de resulta em um erro de C.N tempo de compilação porque nenhum membro nomeado M está no escopo. Os desenvolvedores devem adicionar uma segunda using static diretiva para especificar que os métodos também N1.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 ou internal acessar. O padrão é internal acesso.
  • Os tipos declarados em classes podem ter public, protected internal, protected, private protected, internal, ou private access. O padrão é private acesso.
  • Os tipos declarados em structs podem ter public, internal, ou private 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ₑ>, onde N e I representam identificadores e <A₁, ..., Aₑ> é uma lista de argumentos de tipo. e( é sempre pelo menos um.)
  • N::I, onde N e I 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 identificador global, o namespace global será pesquisado I:
    • Se o namespace global contiver um namespace chamado I e e 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 e e for zero, o qualified_alias_member se referirá a esse tipo.
    • Caso contrário, se o namespace global contiver um tipo chamado I que tenha e 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.
  • 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 chamado I e e 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 chamado I e e for zero, o qualified_alias_member se referirá a esse tipo.
      • Caso contrário, se o namespace associado contiver N um tipo chamado I que tenha e 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.
  • 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 com global::A e o tipo System.Net.Sockets.Socket é referenciado com S::Socket. Usar A.x e S.Socket , em vez disso, teria causado erros em tempo de compilação porque A e S 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 nomeada global no escopo. Se alguma entidade chamada global estivesse no escopo, então global ela global.A teria resolvido para essa entidade.

Usar global como o identificador à esquerda de um qualified_alias_member sempre causa uma pesquisa no global namespace, mesmo que haja um alias using chamado global. 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 e MyGlobalTypes.A global::A resolve para a classe A 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 classe A e o alias A using estão no escopo. Por esse motivo, o uso de A no nome A.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 porque A é pesquisado A :: apenas como um alias de namespace.

exemplo de fim