Compartir a través de


14 Espacios de nombres

14.1 General

Los programas de C# se organizan mediante espacios de nombres. Los espacios de nombres se usan como un sistema de organización "interno" para un programa y como un sistema de organización "externo", una forma de presentar elementos de programa que se exponen a otros programas.

Se proporcionan directivas using (§14.5) para facilitar el uso de espacios de nombres.

14.2 Unidades de compilación

Un compilation_unit consta de cero o más extern_alias_directiveseguidos de cero o más using_directiveseguidos de cero o de un global_attributes seguido de cero o más namespace_member_declarations. El compilation_unit define la estructura general de la entrada.

compilation_unit
    : extern_alias_directive* using_directive* global_attributes?
      namespace_member_declaration*
    ;

Un programa de C# consta de una o varias unidades de compilación. Cuando se compila un programa de C#, todas las unidades de compilación se procesan juntas. Por lo tanto, las unidades de compilación pueden depender entre sí, posiblemente de forma circular.

Los extern_alias_directivede una unidad de compilación afectan a los using_directives, global_attributes y namespace_member_declarationde esa unidad de compilación, pero no tienen ningún efecto en otras unidades de compilación.

Los using_directivede una unidad de compilación afectan a los global_attributes y namespace_member_declarationde esa unidad de compilación, pero no tienen ningún efecto en otras unidades de compilación.

El global_attributes (§22.3) de una unidad de compilación permite la especificación de atributos para el ensamblado y el módulo de destino. Los ensamblados y módulos actúan como contenedores físicos para los tipos. Un ensamblado puede constar de varios módulos separados físicamente.

Los namespace_member_declarationde cada unidad de compilación de un programa contribuyen a los miembros a un único espacio de declaración denominado espacio de nombres global.

Ejemplo:

// File A.cs:
class A {}
// File B.cs:
class B {}

Las dos unidades de compilación contribuyen al único espacio de nombres global, en este caso declarando dos clases con los nombres A completos y B. Dado que las dos unidades de compilación contribuyen al mismo espacio de declaración, habría sido un error si cada una contenía una declaración de un miembro con el mismo nombre.

ejemplo final

14.3 Declaraciones de espacio de nombres

Un namespace_declaration consta del espacio de nombres de palabra clave, seguido de un nombre de espacio de nombres y un cuerpo, seguido opcionalmente de un punto y coma.

namespace_declaration
    : 'namespace' qualified_identifier namespace_body ';'?
    ;

qualified_identifier
    : identifier ('.' identifier)*
    ;

namespace_body
    : '{' extern_alias_directive* using_directive*
      namespace_member_declaration* '}'
    ;

Un namespace_declaration puede producirse como una declaración de nivel superior en una compilation_unit o como una declaración de miembro dentro de otro namespace_declaration. Cuando un namespace_declaration se produce como una declaración de nivel superior en un compilation_unit, el espacio de nombres se convierte en miembro del espacio de nombres global. Cuando se produce un namespace_declaration dentro de otra namespace_declaration, el espacio de nombres interno se convierte en miembro del espacio de nombres externo. En cualquier caso, el nombre de un espacio de nombres será único dentro del espacio de nombres contenedor.

Los espacios de nombres son implícitamente public y la declaración de un espacio de nombres no puede incluir ningún modificador de acceso.

Dentro de un namespace_body, los using_directiveopcionales importan los nombres de otros espacios de nombres, tipos y miembros, lo que les permite hacer referencia directamente en lugar de a través de nombres calificados. Los namespace_member_declarationopcionales contribuyen a los miembros del espacio de declaraciones del espacio de nombres. Tenga en cuenta que todas las using_directiveaparecerán antes de cualquier declaración miembro.

El qualified_identifier de un namespace_declaration puede ser un identificador único o una secuencia de identificadores separados por tokens ".". Este último formulario permite a un programa definir un espacio de nombres anidado sin anidar léxicamente varias declaraciones de espacio de nombres.

Ejemplo:

namespace N1.N2
{
    class A {}
    class B {}
}

es semánticamente equivalente a

namespace N1
{
    namespace N2
    {
        class A {}
        class B {}
    }
}

ejemplo final

Los espacios de nombres están abiertos y dos declaraciones de espacio de nombres con el mismo nombre completo (§7.8.2) contribuyen al mismo espacio de declaración (§7.3).

Ejemplo: en el código siguiente

namespace N1.N2
{
    class A {}
}

namespace N1.N2
{
    class B {}
}

las dos declaraciones de espacio de nombres anteriores contribuyen al mismo espacio de declaración, en este caso declarando dos clases con los nombres N1.N2.A completos y N1.N2.B. Dado que las dos declaraciones contribuyen al mismo espacio de declaración, habría sido un error si cada una contenía una declaración de un miembro con el mismo nombre.

ejemplo final

14.4 Directivas de alias extern

Un extern_alias_directive introduce un identificador que actúa como alias para un espacio de nombres. La especificación del espacio de nombres con alias es externa al código fuente del programa y también se aplica a los espacios de nombres anidados del espacio de nombres con alias.

extern_alias_directive
    : 'extern' 'alias' identifier ';'
    ;

El ámbito de un extern_alias_directive se extiende a través de los using_directive s, global_attributes y namespace_member_declarationde su compilation_unit o namespace_body inmediatamente.

Dentro de una unidad de compilación o un cuerpo del espacio de nombres que contiene un extern_alias_directive, el identificador introducido por el extern_alias_directive se puede usar para hacer referencia al espacio de nombres con alias. Es un error en tiempo de compilación para que el identificador sea la palabra global.

El alias introducido por un extern_alias_directive es muy similar al alias introducido por un using_alias_directive. Consulte §14.5.2 para obtener una explicación más detallada de extern_alias_directives y using_alias_directives.

alias es una palabra clave contextual (§6.4.4) y solo tiene un significado especial cuando sigue inmediatamente la extern palabra clave en un extern_alias_directive.

Se produce un error si un programa declara un alias extern para el que no se proporciona ninguna definición externa.

Ejemplo: el siguiente programa declara y usa dos alias extern, X y Y, cada uno de los cuales representa la raíz de una jerarquía de espacios de nombres distinto:

extern alias X;
extern alias Y;

class Test
{
    X::N.A a;
    X::N.B b1;
    Y::N.B b2;
    Y::N.C c;
}

El programa declara la existencia de los alias X de extern y Y, pero las definiciones reales de los alias son externas al programa. Ahora se puede hacer referencia a las clases con N.B nombre idéntico como X.N.B y Y.N.B, o , mediante el calificador X::N.B de alias de espacio de nombres y Y::N.B. ejemplo final

14.5 Uso de directivas

14.5.1 General

Las directivas using facilitan el uso de espacios de nombres y tipos definidos en otros espacios de nombres. El uso de directivas afecta al proceso de resolución de nombres de namespace_or_type_name s (§7.8) y simple_names (§12.8.4), pero a diferencia de las declaraciones, using_directives no contribuyen nuevos miembros a los espacios de declaración subyacentes de las unidades de compilación o los espacios de nombres dentro de losque se usan.

using_directive
    : using_alias_directive
    | using_namespace_directive
    | using_static_directive    
    ;

Un using_alias_directive (§14.5.2) presenta un alias para un espacio de nombres o tipo.

Un using_namespace_directive (§14.5.3) importa los miembros de tipo de un espacio de nombres.

Un using_static_directive (§14.5.4) importa los tipos anidados y los miembros estáticos de un tipo.

El ámbito de un using_directive se extiende a través del namespace_member_declarations de su unidad de compilación o cuerpo del espacio de nombres inmediatamente. El ámbito de un using_directive específicamente no incluye sus using_directivedel mismo nivel. Por lo tanto, la using_directivedel mismo nivel no afecta entre sí y el orden en el que se escriben es insignificante. En cambio, el ámbito de un extern_alias_directive incluye los using_directivedefinidos en la misma unidad de compilación o cuerpo del espacio de nombres.

14.5.2 Uso de directivas de alias

Un using_alias_directive introduce un identificador que actúa como alias para un espacio de nombres o un tipo dentro de la unidad de compilación o el cuerpo del espacio de nombres incluido inmediatamente.

using_alias_directive
    : 'using' identifier '=' namespace_or_type_name ';'
    ;

Dentro de atributos globales y declaraciones de miembro en una unidad de compilación o un cuerpo de espacio de nombres que contiene un using_alias_directive, el identificador introducido por el using_alias_directive se puede usar para hacer referencia al espacio de nombres o al tipo especificados.

Ejemplo:

namespace N1.N2
{
    class A {}
}
namespace N3
{
    using A = N1.N2.A;

    class B: A {}
}

Anteriormente, dentro de las declaraciones de miembro del N3 espacio de nombres , A es un alias para N1.N2.Ay, por tanto, la clase N3.B deriva de la clase N1.N2.A. El mismo efecto se puede obtener creando un alias R para N1.N2 y haciendo referencia a R.A:

namespace N3
{
    using R = N1.N2;

    class B : R.A {}
}

ejemplo final

Dentro de las directivas using, los atributos globales y las declaraciones de miembro en un cuerpo de unidad de compilación o espacio de nombres que contiene un extern_alias_directive, el identificador introducido por el extern_alias_directive se puede usar para hacer referencia al espacio de nombres asociado.

Ejemplo: por ejemplo:

namespace N1
{
    extern alias N2;

    class B : N2::A {}
}

Anteriormente, dentro de las declaraciones de miembro del N1 espacio de nombres, N2 es un alias para algún espacio de nombres cuya definición es externa al código fuente del programa. La clase N1.B deriva de la clase N2.A. El mismo efecto se puede obtener creando un alias A para N2.A y haciendo referencia a A:

namespace N1
{
    extern alias N2;

    using A = N2::A;

    class B : A {}
}

ejemplo final

Un extern_alias_directive o using_alias_directive hace que un alias esté disponible dentro de una unidad de compilación determinada o un cuerpo del espacio de nombres, pero no contribuye a ningún nuevo miembro al espacio de declaración subyacente. En otras palabras, una directiva de alias no es transitiva, sino que solo afecta al cuerpo de la unidad de compilación o del espacio de nombres en el que se produce.

Ejemplo: en el código siguiente

namespace N3
{
    extern alias R1;

    using R2 = N1.N2;
}

namespace N3
{
    class B : R1::A, R2.I {} // Error, R1 and R2 unknown
}

los ámbitos de las directivas de alias que introducen R1 y R2 solo se extienden a las declaraciones de miembro en el cuerpo del espacio de nombres en el que se encuentran, por lo que R1 y R2 se desconocen en la segunda declaración de espacio de nombres. Sin embargo, colocar las directivas de alias en la unidad de compilación contenedora hace que el alias esté disponible en ambas declaraciones de espacio de nombres:

extern alias R1;

using R2 = N1.N2;

namespace N3
{
    class B : R1::A, R2.I {}
}

namespace N3
{
    class C : R1::A, R2.I {}
}

ejemplo final

Cada extern_alias_directive o using_alias_directive en un compilation_unit o namespace_body contribuye a un nombre al espacio de declaración de alias (§7.3) de la compilation_unit o namespace_body inmediatamente envolventes. El identificador de la directiva de alias será único dentro del espacio de declaración de alias correspondiente. El identificador de alias no debe ser único dentro del espacio de declaración global o el espacio de declaración del espacio de nombres correspondiente.

Ejemplo:

extern alias X;
extern alias Y;

using X = N1.N2; // Error: alias X already exists

class Y {} // Ok

El alias que usa el nombre X produce un error, ya que ya hay un alias denominado X en la misma unidad de compilación. La clase denominada Y no entra en conflicto con el alias extern denominado Y , ya que estos nombres se agregan a espacios de declaración distintos. El primero se agrega al espacio de declaración global y este último se agrega al espacio de declaración de alias para esta unidad de compilación.

Cuando un nombre de alias coincide con el nombre de un miembro de un espacio de nombres, el uso de cualquiera de los dos se calificará adecuadamente:

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
}

En el segundo cuerpo del espacio de nombres para N3, el uso no completo de B da como resultado un error, ya que N3 contiene un miembro denominado B y el cuerpo del espacio de nombres que también declara un alias con nombre B; del mismo modo para A. Se puede hacer referencia a la clase N3.B como N3.B o global::N3.B. El alias A se puede usar en un miembro calificado de alias (§14.8), como A::B. El alias B es esencialmente inútil. No se puede usar en un qualified_alias_member , ya que solo se pueden usar alias de espacio de nombres en un qualified_alias_member y B alias de un tipo.

ejemplo final

Al igual que los miembros normales, los nombres introducidos por alias_directives están ocultos por miembros con nombre similar en ámbitos anidados.

Ejemplo: en el código siguiente

using R = N1.N2;

namespace N3
{
    class R {}
    class B: R.A {} // Error, R has no member A
}

la referencia a R.A en la declaración de produce un error en tiempo de B compilación porque R hace referencia a N3.R, no N1.N2a .

ejemplo final

El orden en que se escriben extern_alias_directive no tiene importancia. Del mismo modo, el orden en que se escriben using_alias_directives no tiene ningún significado, pero todos los using_alias_directives vendrán después de todos los extern_alias_directiveen la misma unidad de compilación o cuerpo del espacio de nombres. La resolución de la namespace_or_type_name a la que hace referencia un using_alias_directive no se ve afectada por el propio using_alias_directive ni por otros using_directiveen el cuerpo de la unidad de compilación o espacio de nombres inmediatamente que contiene, pero puede verse afectado por extern_alias_directiveen el cuerpo de espacio de nombres o unidad de compilación que contiene inmediatamente. Es decir, el namespace_or_type_name de un using_alias_directive se resuelve como si el cuerpo de la unidad de compilación o del espacio de nombres que contiene inmediatamente no tuviera using_directive s, pero tiene el conjunto correcto de extern_alias_directives.

Ejemplo: en el código siguiente

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
}

el último using_alias_directive produce un error en tiempo de compilación porque no se ve afectado por el using_alias_directive anterior. El primer using_alias_directive no produce un error, ya que el ámbito del alias X de extern incluye el using_alias_directive.

ejemplo final

Un using_alias_directive puede crear un alias para cualquier espacio de nombres o tipo, incluido el espacio de nombres en el que aparece y cualquier espacio de nombres o tipo anidado dentro de ese espacio de nombres.

El acceso a un espacio de nombres o un tipo a través de un alias produce exactamente el mismo resultado que el acceso a ese espacio de nombres o tipo a través de su nombre declarado.

Ejemplo: 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
    }
}

los nombres N1.N2.A, R1.N2.Ay son equivalentes y R2.A todos hacen referencia a la declaración de clase cuyo nombre completo es N1.N2.A.

ejemplo final

Aunque cada parte de un tipo parcial (§15.2.7) se declara dentro del mismo espacio de nombres, las partes normalmente se escriben en diferentes declaraciones de espacio de nombres. Por lo tanto, se pueden presentar diferentes extern_alias_directives y using_directivepara cada parte. Al interpretar nombres simples (§12.8.4) dentro de una parte, solo se consideran los extern_alias_directives y using_directivede los cuerpos del espacio de nombres y la unidad de compilación que incluye esa parte. Esto puede dar lugar a que el mismo identificador tenga significados diferentes en partes diferentes.

Ejemplo:

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

ejemplo final

El uso de alias puede asignar un nombre a un tipo construido cerrado, pero no puede asignar un nombre a una declaración de tipo genérico sin proporcionar argumentos de tipo.

Ejemplo:

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
}

ejemplo final

14.5.3 Uso de directivas de espacio de nombres

Un using_namespace_directive importa los tipos contenidos en un espacio de nombres en el cuerpo de espacio de nombres o unidad de compilación envolvente inmediatamente, lo que permite usar el identificador de cada tipo sin cualificación.

using_namespace_directive
    : 'using' namespace_name ';'
    ;

Dentro de las declaraciones de miembro de una unidad de compilación o un cuerpo de espacio de nombres que contiene un using_namespace_directive, se puede hacer referencia directamente a los tipos contenidos en el espacio de nombres especificado.

Ejemplo:

namespace N1.N2
{
    class A {}
}

namespace N3
{
    using N1.N2;

    class B : A {}
}

Anteriormente, dentro de las declaraciones de miembro del N3 espacio de nombres, los miembros de tipo de N1.N2 están disponibles directamente y, por tanto, la clase N3.B deriva de la clase N1.N2.A.

ejemplo final

Un using_namespace_directive importa los tipos contenidos en el espacio de nombres especificado, pero específicamente no importa espacios de nombres anidados.

Ejemplo: en el código siguiente

namespace N1.N2
{
    class A {}
}

namespace N3
{
    using N1;
    class B : N2.A {} // Error, N2 unknown
}

el using_namespace_directive importa los tipos contenidos en N1, pero no los espacios de nombres anidados en N1. Por lo tanto, la referencia a N2.A en la declaración de da como resultado un error en tiempo de B compilación porque ningún miembro denominado N2 está en el ámbito.

ejemplo final

A diferencia de un using_alias_directive, un using_namespace_directive puede importar tipos cuyos identificadores ya están definidos dentro de la unidad de compilación envolvente o el cuerpo del espacio de nombres. En efecto, los nombres importados por un using_namespace_directive están ocultos por miembros con nombre similar en la unidad de compilación envolvente o el cuerpo del espacio de nombres.

Ejemplo:

namespace N1.N2
{
    class A {}
    class B {}
}

namespace N3
{
    using N1.N2;
    class A {}
}

Aquí, dentro de las declaraciones de miembro del N3 espacio de nombres, A hace referencia a N3.A en lugar de N1.N2.Aa .

ejemplo final

Dado que los nombres pueden ser ambiguos cuando más de un espacio de nombres importado introduce el mismo nombre de tipo, un using_alias_directive es útil para eliminar la ambigüedad de la referencia.

Ejemplo: en el código siguiente

namespace N1
{
    class A {}
}

namespace N2
{
    class A {}
}

namespace N3
{
    using N1;
    using N2;

    class B : A {} // Error, A is ambiguous
}

N2 y N1 contienen un miembro A, y porque N3 importa ambos, hacer referencia A a en es un error en N3 tiempo de compilación. En esta situación, el conflicto se puede resolver a través de la calificación de referencias a Ao introduciendo un using_alias_directive que elige un determinado A. Por ejemplo:

namespace N3
{
    using N1;
    using N2;
    using A = N1.A;

    class B : A {} // A means N1.A
}

ejemplo final

Además, cuando más de un espacio de nombres o un tipo importados por using_namespace_directives o using_static_directives en la misma unidad de compilación o cuerpo del espacio de nombres contienen tipos o miembros por el mismo nombre, las referencias a ese nombre que una simple_name se consideran ambiguas.

Ejemplo:

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 contiene un miembro Ade tipo y C contiene un campo Aestático y, dado que N2 importa ambos, hacer referencia A a un simple_name es ambiguo y un error en tiempo de compilación.

ejemplo final

Al igual que un using_alias_directive, un using_namespace_directive no contribuye a ningún nuevo miembro al espacio de declaración subyacente de la unidad de compilación o el espacio de nombres, sino que, en su lugar, afecta solo al cuerpo de la unidad de compilación o del espacio de nombres en el que aparece.

El namespace_name al que hace referencia un using_namespace_directive se resuelve de la misma manera que el namespace_or_type_name al que hace referencia un using_alias_directive. Por lo tanto, using_namespace_directives en la misma unidad de compilación o cuerpo del espacio de nombres no afectan entre sí y se pueden escribir en cualquier orden.

14.5.4 Uso de directivas estáticas

Un using_static_directive importa los tipos anidados y los miembros estáticos contenidos directamente en una declaración de tipo en el cuerpo de espacio de nombres o unidad de compilación envolvente inmediatamente, lo que permite usar el identificador de cada miembro y tipo sin cualificación.

using_static_directive
    : 'using' 'static' type_name ';'
    ;

Dentro de las declaraciones de miembro de una unidad de compilación o un cuerpo de espacio de nombres que contiene un using_static_directive, se puede hacer referencia directamente a los tipos anidados y a los miembros estáticos accesibles (excepto los métodos de extensión) contenidos directamente en la declaración del tipo especificado.

Ejemplo:

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

En el código anterior, dentro de las declaraciones de miembro del N2 espacio de nombres, los miembros estáticos y los tipos anidados de N1.A están disponibles directamente y, por tanto, el método N puede hacer referencia a los B miembros y M de N1.A.

ejemplo final

Un using_static_directive específicamente no importa métodos de extensión directamente como métodos estáticos, pero hace que estén disponibles para la invocación del método de extensión (§12.8.9.3).

Ejemplo:

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

el using_static_directive importa el método M de extensión contenido en N1.A, pero solo como un método de extensión. Por lo tanto, la primera referencia a M en el cuerpo de da como resultado un error en tiempo de B.N compilación porque ningún miembro denominado M está en el ámbito.

ejemplo final

Un using_static_directive solo importa miembros y tipos declarados directamente en el tipo especificado, no miembros y tipos declarados en clases base.

Ejemplo:

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

el using_static_directive importa el método M2 contenido en N1.B, pero no importa el método M contenido en N1.A. Por lo tanto, la referencia a M en el cuerpo de da como resultado un error en tiempo de C.N compilación porque ningún miembro denominado M está en el ámbito. Los desarrolladores deben agregar una segunda using static directiva para especificar que los métodos de N1.A también deben importarse.

ejemplo final

Las ambigüedades entre varios using_namespace_directives y using_static_directives se describen en §14.5.3.

14.6 Declaraciones de miembro de espacio de nombres

Un namespace_member_declaration es un namespace_declaration (§14.3) o un type_declaration (§14.7).

namespace_member_declaration
    : namespace_declaration
    | type_declaration
    ;

Una unidad de compilación o un cuerpo de espacio de nombres puede contener namespace_member_declarations, y estas declaraciones contribuyen a nuevos miembros al espacio de declaración subyacente de la unidad de compilación o el cuerpo del espacio de nombres.

14.7 Declaraciones de tipo

Un type_declaration es un class_declaration (§15.2), un struct_declaration (§16.2), un interface_declaration (§18.2), un enum_declaration (§19.2) o un delegate_declaration (§20.2).

type_declaration
    : class_declaration
    | struct_declaration
    | interface_declaration
    | enum_declaration
    | delegate_declaration
    ;

Un type_declaration puede producirse como una declaración de nivel superior en una unidad de compilación o como una declaración de miembro dentro de un espacio de nombres, una clase o una estructura.

Cuando se produce una declaración de tipo para un tipo T como una declaración de nivel superior en una unidad de compilación, el nombre completo (§7.8.2) de la declaración de tipo es el mismo que el nombre no completo de la declaración (§7.8.2). Cuando se produce una declaración de tipo para un tipo T dentro de una declaración de espacio de nombres, clase o estructura, el nombre completo (§7.8.3) de la declaración S.Nde tipo , donde S es el nombre completo del espacio de nombres contenedor, la clase o la declaración de estructura, y N es el nombre no completo de la declaración.

Un tipo declarado dentro de una clase o estructura se denomina tipo anidado (§15.3.9).

Los modificadores de acceso permitidos y el acceso predeterminado para una declaración de tipo dependen del contexto en el que tiene lugar la declaración (§7.5.2):

  • Los tipos declarados en unidades de compilación o espacios de nombres pueden tener public o internal tener acceso. El valor predeterminado es internal el acceso.
  • Los tipos declarados en clases pueden tener publicacceso a , protected internal, protected, private protected, , internalo private . El valor predeterminado es private el acceso.
  • Los tipos declarados en estructuras pueden tener publicacceso a , internalo private . El valor predeterminado es private el acceso.

14.8 Miembro de alias calificado

14.8.1 General

El calificador :: de alias de espacio de nombres permite garantizar que las búsquedas de nombres de tipo no se ven afectadas por la introducción de nuevos tipos y miembros. El calificador de alias de espacio de nombres siempre aparece entre dos identificadores denominados identificadores izquierdo y derecho. A diferencia del calificador normal . , el identificador izquierdo del :: calificador solo se busca como un extern o mediante alias.

Un qualified_alias_member proporciona acceso explícito al espacio de nombres global y a extern o mediante alias que pueden ocultar otras entidades.

qualified_alias_member
    : identifier '::' identifier type_argument_list?
    ;

Un qualified_alias_member se puede usar como namespace_or_type_name (§7.8) o como operando izquierdo en un member_access (§12.8.7).

Un qualified_alias_member consta de dos identificadores, denominados identificadores izquierdo y derecho, separados por el :: token y, opcionalmente, seguidos de un type_argument_list. Cuando el identificador izquierdo es global, el espacio de nombres global se busca en el identificador derecho. Para cualquier otro identificador izquierdo, ese identificador se busca como un extern o mediante alias (§14.4 y §14.5.2). Se produce un error en tiempo de compilación si no hay ese alias o el alias hace referencia a un tipo. Si el alias hace referencia a un espacio de nombres, ese espacio de nombres se busca en el identificador derecho.

Un qualified_alias_member tiene una de estas dos formas:

  • N::I<A₁, ..., Aₑ>, donde N y I representan identificadores, y <A₁, ..., Aₑ> es una lista de argumentos de tipo. (e siempre es al menos uno).
  • N::I, donde N y I representan identificadores. (En este caso, e se considera cero).

Con esta notación, el significado de un qualified_alias_member se determina de la siguiente manera:

  • Si N es el identificador global, se busca Ien el espacio de nombres global :
    • Si el espacio de nombres global contiene un espacio de nombres denominado I y e es cero, el qualified_alias_member hace referencia a ese espacio de nombres.
    • De lo contrario, si el espacio de nombres global contiene un tipo no genérico denominado I y e es cero, el qualified_alias_member hace referencia a ese tipo.
    • De lo contrario, si el espacio de nombres global contiene un tipo denominado I que tiene e parámetros de tipo, el qualified_alias_member hace referencia a ese tipo construido con los argumentos de tipo especificados.
    • De lo contrario, el qualified_alias_member no está definido y se produce un error en tiempo de compilación.
  • De lo contrario, a partir de la declaración de espacio de nombres (§14.3) que contiene inmediatamente el qualified_alias_member (si existe), continuando con cada declaración de espacio de nombres envolvente (si existe) y finalizando con la unidad de compilación que contiene el qualified_alias_member, se evalúan los pasos siguientes hasta que se encuentra una entidad:
    • Si la declaración de espacio de nombres o la unidad de compilación contiene un using_alias_directive que asocia N a un tipo, el qualified_alias_member no está definido y se produce un error en tiempo de compilación.
    • De lo contrario, si la declaración de espacio de nombres o la unidad de compilación contiene un extern_alias_directive o using_alias_directive que se asocia N a un espacio de nombres, a continuación:
      • Si el espacio de nombres asociado a N contiene un espacio de nombres denominado I y e es cero, el qualified_alias_member hace referencia a ese espacio de nombres.
      • De lo contrario, si el espacio de nombres asociado a N contiene un tipo no genérico denominado I y e es cero, el qualified_alias_member hace referencia a ese tipo.
      • De lo contrario, si el espacio de nombres asociado a N contiene un tipo denominado I que tiene e parámetros de tipo, el qualified_alias_member hace referencia a ese tipo construido con los argumentos de tipo especificados.
      • De lo contrario, el qualified_alias_member no está definido y se produce un error en tiempo de compilación.
  • De lo contrario, el qualified_alias_member no está definido y se produce un error en tiempo de compilación.

Ejemplo: en el 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;
    }
}

se hace referencia a la clase A con global::A y se hace referencia al tipo System.Net.Sockets.Socket con S::Socket. El uso A.x de y S.Socket en su lugar habría provocado errores en tiempo de compilación porque A y S se habrían resuelto en los parámetros.

ejemplo final

Nota: El identificador global tiene un significado especial solo cuando se usa como identificador izquierdo de un qualified_alias_name. No es una palabra clave y no es en sí mismo un alias; es una palabra clave contextual (§6.4.4). En el 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 produce un error en tiempo de compilación, ya que no hay ninguna entidad denominada global en el ámbito. Si alguna entidad denominada global estuviera en el ámbito, global en global.A habría resuelto esa entidad.

El uso global de como identificador izquierdo de un qualified_alias_member siempre provoca una búsqueda en el global espacio de nombres, incluso si hay un alias con el nombre global. En el 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 se resuelve en MyGlobalTypes.A y global::A se resuelve en la clase A en el espacio de nombres global.

nota final

14.8.2 Unicidad de alias

Cada unidad de compilación y cuerpo del espacio de nombres tiene un espacio de declaración independiente para alias extern y el uso de alias. Por lo tanto, aunque el nombre de un alias extern o el uso de alias debe ser único dentro del conjunto de alias extern y el uso de alias declarados en el cuerpo de la unidad de compilación o espacio de nombres inmediatamente que contiene, se permite que un alias tenga el mismo nombre que un tipo o espacio de nombres siempre que solo se use con el :: calificador.

Ejemplo: En lo siguiente:

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

el nombre A tiene dos significados posibles en el segundo cuerpo del espacio de nombres porque tanto la clase A como el alias A using están en el ámbito. Por este motivo, el uso de A en el nombre A.Stream completo es ambiguo y hace que se produzca un error en tiempo de compilación. Sin embargo, el uso de A con el :: calificador no es un error porque A solo se busca como alias de espacio de nombres.

ejemplo final