Condividi tramite


14 Spazi dei nomi

14.1 Generale

I programmi C# sono organizzati usando spazi dei nomi. Gli spazi dei nomi vengono usati sia come sistema di organizzazione "interno" per un programma che come sistema dell'organizzazione "esterno", un modo per presentare gli elementi del programma esposti ad altri programmi.

Le direttive using (§14.5) sono fornite per facilitare l'uso degli spazi dei nomi.

14.2 Unità di compilazione

Un compilation_unit è costituito da zero o più extern_alias_directiveseguito da zero o più using_directiveseguito da zero o da un global_attributes seguito da zero o più namespace_member_declarations. Il compilation_unit definisce la struttura complessiva dell'input.

compilation_unit
    : extern_alias_directive* using_directive* global_attributes?
      namespace_member_declaration*
    ;

Un programma C# è costituito da una o più unità di compilazione. Quando un programma C# viene compilato, tutte le unità di compilazione vengono elaborate insieme. Pertanto, le unità di compilazione possono dipendere l'una dall'altra, possibilmente in modo circolare.

I extern_alias_directivedi un'unità di compilazione influiscono sui using_directive, global_attributes e namespace_member_declaration di tale unità di compilazione, ma non hanno alcun effetto su altre unità di compilazione.

I using_directivedi un'unità di compilazione influiscono sui global_attributes e sui namespace_member_declaration di tale unità di compilazione, ma non hanno alcun effetto su altre unità di compilazione.

Il global_attributes (§22.3) di un'unità di compilazione consente la specifica degli attributi per l'assembly e il modulo di destinazione. Gli assembly e i moduli fungono da contenitori fisici per i tipi. Un assembly può essere costituito da diversi moduli separati fisicamente.

I namespace_member_declarationdi ogni unità di compilazione di un programma contribuiscono ai membri di un singolo spazio di dichiarazione denominato spazio dei nomi globale.

Esempio:

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

Le due unità di compilazione contribuiscono al singolo spazio dei nomi globale, in questo caso dichiarando due classi con i nomi A completi e B. Poiché le due unità di compilazione contribuiscono allo stesso spazio di dichiarazione, sarebbe stato un errore se ognuna conteneva una dichiarazione di un membro con lo stesso nome.

esempio finale

14.3 Dichiarazioni dello spazio dei nomi

Un namespace_declaration è costituito dallo spazio dei nomi della parola chiave, seguito da un nome e un corpo dello spazio dei nomi, eventualmente seguito da un punto e virgola.

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

qualified_identifier
    : identifier ('.' identifier)*
    ;

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

Un namespace_declaration può verificarsi come dichiarazione di primo livello in un compilation_unit o come dichiarazione di membro all'interno di un altro namespace_declaration. Quando un namespace_declaration si verifica come dichiarazione di primo livello in un compilation_unit, lo spazio dei nomi diventa membro dello spazio dei nomi globale. Quando un namespace_declaration si verifica all'interno di un altro namespace_declaration, lo spazio dei nomi interno diventa membro dello spazio dei nomi esterno. In entrambi i casi, il nome di uno spazio dei nomi deve essere univoco all'interno dello spazio dei nomi contenitore.

Gli spazi dei nomi sono implicitamente public e la dichiarazione di uno spazio dei nomi non può includere modificatori di accesso.

All'interno di un namespace_body, l'using_directivefacoltativo importa i nomi di altri spazi dei nomi, tipi e membri, consentendo di farvi riferimento direttamente anziché tramite nomi qualificati. Il namespace_member_declarationfacoltativo contribuisce ai membri dello spazio dei nomi della dichiarazione. Si noti che tutte le using_directivedevono essere visualizzate prima di qualsiasi dichiarazione di membro.

Il qualified_identifier di un namespace_declaration può essere un singolo identificatore o una sequenza di identificatori separati da token ".". Quest'ultimo modulo consente a un programma di definire uno spazio dei nomi annidato senza annidare in modo lessicale diverse dichiarazioni di spazio dei nomi.

Esempio:

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

è semanticamente equivalente a

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

esempio finale

Gli spazi dei nomi sono aperti e due dichiarazioni dello spazio dei nomi con lo stesso nome completo (§7.8.2) contribuiscono allo stesso spazio di dichiarazione (§7.3).

Esempio: nel codice seguente

namespace N1.N2
{
    class A {}
}

namespace N1.N2
{
    class B {}
}

le due dichiarazioni dello spazio dei nomi precedenti contribuiscono allo stesso spazio di dichiarazione, in questo caso dichiarando due classi con i nomi N1.N2.A completi e N1.N2.B. Poiché le due dichiarazioni contribuiscono allo stesso spazio di dichiarazione, sarebbe stato un errore se ognuna contenga una dichiarazione di un membro con lo stesso nome.

esempio finale

14.4 Direttive alias extern

Un extern_alias_directive introduce un identificatore che funge da alias per uno spazio dei nomi. La specifica dello spazio dei nomi con alias è esterna al codice sorgente del programma e si applica anche agli spazi dei nomi annidati dello spazio dei nomi con alias.

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

L'ambito di un extern_alias_directive si estende su using_directive, global_attributes e namespace_member_declaration dei relativi compilation_unito namespace_body immediatamente contenuti.

All'interno di un'unità di compilazione o di un corpo dello spazio dei nomi che contiene un extern_alias_directive, è possibile usare l'identificatore introdotto dal extern_alias_directive per fare riferimento allo spazio dei nomi con alias. Si tratta di un errore in fase di compilazione affinché l'identificatore sia la parola global.

L'alias introdotto da un extern_alias_directive è molto simile all'alias introdotto da un using_alias_directive. Per informazioni più dettagliate su extern_alias_directive s e using_alias_directive, vedere §14.5.2.

alias è una parola chiave contestuale (§6.4.4) e ha un significato speciale solo quando segue immediatamente la extern parola chiave in un extern_alias_directive.

Si verifica un errore se un programma dichiara un alias extern per il quale non viene fornita alcuna definizione esterna.

Esempio: il programma seguente dichiara e usa due alias X extern e Y, ognuno dei quali rappresenta la radice di una gerarchia di spazi dei nomi 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;
}

Il programma dichiara l'esistenza degli alias X extern e Y, ma le definizioni effettive degli alias sono esterne al programma. È ora possibile fare riferimento alle classi denominate N.B in modo identico come X.N.B e Y.N.Boppure usando il qualificatore X::N.B alias dello spazio dei nomi e Y::N.B. esempio finale

14.5 Direttive Using

14.5.1 Generale

L'uso delle direttive facilita l'uso di spazi dei nomi e tipi definiti in altri spazi dei nomi. L'utilizzo delle direttive influisce sul processo di risoluzione dei nomi di namespace_or_type_name (§7.8) e simple_names (§12.8.4), ma a differenza delle dichiarazioni, using_directivenon contribuiscono nuovi membri agli spazi di dichiarazione sottostanti delle unità di compilazione o degli spazi dei nomi in cui vengono utilizzati.

using_directive
    : using_alias_directive
    | using_namespace_directive
    | using_static_directive    
    ;

Un using_alias_directive (§14.5.2) introduce un alias per uno spazio dei nomi o un tipo.

Un using_namespace_directive (§14.5.3) importa i membri di tipo di uno spazio dei nomi.

Un using_static_directive (§14.5.4) importa i tipi annidati e i membri statici di un tipo.

L'ambito di un using_directive estende il namespace_member_declarations del relativo corpo immediatamente contenente l'unità di compilazione o lo spazio dei nomi. L'ambito di un using_directive in particolare non include i relativi using_directivepeer. Pertanto, il peer using_directives non influiscono l'uno sull'altro e l'ordine in cui sono scritti è insignificante. Al contrario, l'ambito di un extern_alias_directive include i using_directivedefiniti nella stessa unità di compilazione o nello stesso corpo dello spazio dei nomi.

14.5.2 Uso delle direttive alias

Un using_alias_directive introduce un identificatore che funge da alias per uno spazio dei nomi o un tipo all'interno dell'unità di compilazione o del corpo dello spazio dei nomi che lo racchiude immediatamente.

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

All'interno di attributi globali e dichiarazioni di membri in un'unità di compilazione o in un corpo dello spazio dei nomi che contiene un using_alias_directive, è possibile usare l'identificatore introdotto dal using_alias_directive per fare riferimento allo spazio dei nomi o al tipo specificato.

Esempio:

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

    class B: A {}
}

In precedenza, all'interno delle dichiarazioni membro nello spazio dei N3 nomi , A è un alias per N1.N2.Ae quindi la classe N3.B deriva dalla classe N1.N2.A. Lo stesso effetto può essere ottenuto creando un alias R per N1.N2 e quindi facendo riferimento a R.A:

namespace N3
{
    using R = N1.N2;

    class B : R.A {}
}

esempio finale

All'interno delle direttive using, gli attributi globali e le dichiarazioni dei membri in un'unità di compilazione o in un corpo dello spazio dei nomi che contiene un extern_alias_directive, l'identificatore introdotto dal extern_alias_directive può essere usato per fare riferimento allo spazio dei nomi associato.

Esempio: ad esempio:

namespace N1
{
    extern alias N2;

    class B : N2::A {}
}

In precedenza, all'interno delle dichiarazioni dei membri nello N1 spazio dei nomi, N2 è un alias per alcuni spazi dei nomi la cui definizione è esterna al codice sorgente del programma. La classe N1.B deriva dalla classe N2.A. Lo stesso effetto può essere ottenuto creando un alias A per N2.A e quindi facendo riferimento a A:

namespace N1
{
    extern alias N2;

    using A = N2::A;

    class B : A {}
}

esempio finale

Un extern_alias_directive o using_alias_directive rende disponibile un alias all'interno di una particolare unità di compilazione o corpo dello spazio dei nomi, ma non contribuisce ad alcun nuovo membro allo spazio di dichiarazione sottostante. In altre parole, una direttiva alias non è transitiva, ma invece influisce solo sull'unità di compilazione o sul corpo dello spazio dei nomi in cui si verifica.

Esempio: nel codice seguente

namespace N3
{
    extern alias R1;

    using R2 = N1.N2;
}

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

gli ambiti delle direttive alias che introducono R1 e R2 si estendono solo alle dichiarazioni membro nel corpo dello spazio dei nomi in cui sono contenuti, quindi R1 sono R2 sconosciuti nella seconda dichiarazione dello spazio dei nomi. Tuttavia, inserendo le direttive alias nell'unità di compilazione contenitore, l'alias diventa disponibile all'interno di entrambe le dichiarazioni dello spazio dei nomi:

extern alias R1;

using R2 = N1.N2;

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

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

esempio finale

Ogni extern_alias_directive o using_alias_directive in un compilation_unit o namespace_body contribuisce a un nome allo spazio di dichiarazione alias (§7.3) del compilation_unit o namespace_body immediatamente racchiuso. L'identificatore della direttiva alias deve essere univoco all'interno dello spazio di dichiarazione alias corrispondente. L'identificatore alias non deve essere univoco nello spazio di dichiarazione globale o nello spazio dei nomi corrispondente.

Esempio:

extern alias X;
extern alias Y;

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

class Y {} // Ok

L'alias using named X causa un errore perché esiste già un alias denominato X nella stessa unità di compilazione. La classe denominata Y non è in conflitto con l'alias extern denominato Y perché questi nomi vengono aggiunti a spazi di dichiarazione distinti. Il primo viene aggiunto allo spazio di dichiarazione globale e quest'ultimo viene aggiunto allo spazio di dichiarazione alias per questa unità di compilazione.

Quando un nome alias corrisponde al nome di un membro di uno spazio dei nomi, l'utilizzo di uno dei due deve essere qualificato in modo appropriato:

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
}

Nel secondo corpo dello spazio dei nomi per N3, l'uso non qualificato di B restituisce un errore, poiché N3 contiene un membro denominato B e il corpo dello spazio dei nomi che dichiara anche un alias con nome B; allo stesso modo per A. È possibile fare riferimento alla classe N3.B come N3.B o global::N3.B. L'alias A può essere usato in un membro alias qualificato (§14.8), ad esempio A::B. L'alias B è essenzialmente inutile. Non può essere usato in un qualified_alias_member perché solo gli alias dello spazio dei nomi possono essere usati in un qualified_alias_member e B alias un tipo.

esempio finale

Analogamente ai membri regolari, i nomi introdotti da alias_directives sono nascosti da membri denominati in modo analogo negli ambiti annidati.

Esempio: nel codice seguente

using R = N1.N2;

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

Il riferimento a nella dichiarazione di causa un errore in fase di compilazione perché R fa riferimento a R.A , non N1.N2a N3.R.B

esempio finale

L'ordine in cui extern_alias_directivesono scritti non ha alcun significato. Analogamente, l'ordine in cui vengono scritti using_alias_directives non ha alcun significato, ma tutte le using_alias_directives devono venire dopo tutte le extern_alias_directive s nella stessa unità di compilazione o nello stesso corpo dello spazio dei nomi. La risoluzione del namespace_or_type_name a cui fa riferimento un using_alias_directive non è influenzata dal using_alias_directive stesso o da altri using_directivenell'unità di compilazione o dal corpo dello spazio dei nomi che lo contiene immediatamente, ma può essere influenzata da extern_alias_directives nell'unità di compilazione o nel corpo dello spazio dei nomi che lo contiene immediatamente. In altre parole, il namespace_or_type_name di un using_alias_directive viene risolto come se il corpo dell'unità di compilazione o dello spazio dei nomi contenente immediatamente non aveva using_directives, ma ha il set corretto di extern_alias_directives.

Esempio: nel codice seguente

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
}

l'ultimo using_alias_directive genera un errore in fase di compilazione perché non è interessato dal using_alias_directive precedente. Il primo using_alias_directive non genera un errore perché l'ambito dell'alias X extern include il using_alias_directive.

esempio finale

Un using_alias_directive può creare un alias per qualsiasi spazio dei nomi o tipo, incluso lo spazio dei nomi in cui viene visualizzato e qualsiasi spazio dei nomi o tipo annidato all'interno di tale spazio dei nomi.

L'accesso a uno spazio dei nomi o un tipo tramite un alias restituisce esattamente lo stesso risultato dell'accesso allo spazio dei nomi o al tipo tramite il nome dichiarato.

Esempio: Dato

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

i nomi N1.N2.A, R1.N2.Ae R2.A sono equivalenti e tutti fanno riferimento alla dichiarazione di classe il cui nome completo è N1.N2.A.

esempio finale

Anche se ogni parte di un tipo parziale (§15.2.7) viene dichiarata all'interno dello stesso spazio dei nomi, le parti vengono in genere scritte all'interno di dichiarazioni dello spazio dei nomi diverse. Pertanto, per ogni parte possono essere presenti extern_alias_directive e using_directives diversi. Quando si interpretano nomi semplici (§12.8.4) all'interno di una parte, vengono considerati solo i extern_alias_directive e i using_directivedei corpi dello spazio dei nomi e dell'unità di compilazione che racchiude tale parte. Ciò può comportare lo stesso identificatore con significati diversi in parti diverse.

Esempio:

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

esempio finale

L'uso di alias può denominare un tipo costruito chiuso, ma non può denominare una dichiarazione di tipo generico non associato senza fornire argomenti di tipo.

Esempio:

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
}

esempio finale

14.5.3 Uso delle direttive dello spazio dei nomi

Un using_namespace_directive importa i tipi contenuti in uno spazio dei nomi nell'unità di compilazione o nel corpo dello spazio dei nomi che racchiude immediatamente, consentendo l'identificatore di ogni tipo da usare senza qualifica.

using_namespace_directive
    : 'using' namespace_name ';'
    ;

All'interno delle dichiarazioni dei membri in un'unità di compilazione o in un corpo dello spazio dei nomi che contiene un using_namespace_directive, è possibile fare riferimento direttamente ai tipi contenuti nello spazio dei nomi specificato.

Esempio:

namespace N1.N2
{
    class A {}
}

namespace N3
{
    using N1.N2;

    class B : A {}
}

In precedenza, all'interno delle dichiarazioni membro nello spazio dei N3 nomi, i membri di tipo di N1.N2 sono direttamente disponibili e quindi la classe N3.B deriva dalla classe N1.N2.A.

esempio finale

Un using_namespace_directive importa i tipi contenuti nello spazio dei nomi specificato, ma in particolare non importa spazi dei nomi annidati.

Esempio: nel codice seguente

namespace N1.N2
{
    class A {}
}

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

il using_namespace_directive importa i tipi contenuti in N1, ma non gli spazi dei nomi annidati in N1. Di conseguenza, il riferimento a N2.A nella dichiarazione di genera un errore in fase di B compilazione perché nessun membro denominato N2 è nell'ambito.

esempio finale

A differenza di un using_alias_directive, un using_namespace_directive può importare tipi i cui identificatori sono già definiti all'interno dell'unità di compilazione o del corpo dello spazio dei nomi. In effetti, i nomi importati da un using_namespace_directive sono nascosti da membri denominati in modo simile nell'unità di compilazione o nel corpo dello spazio dei nomi che lo racchiude.

Esempio:

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

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

In questo caso, all'interno delle dichiarazioni dei membri nello spazio dei N3 nomi , A fa riferimento a anziché N1.N2.Aa N3.A .

esempio finale

Poiché i nomi possono essere ambigui quando più di uno spazio dei nomi importato introduce lo stesso nome di tipo, un using_alias_directive è utile per disambiguare il riferimento.

Esempio: nel codice seguente

namespace N1
{
    class A {}
}

namespace N2
{
    class A {}
}

namespace N3
{
    using N1;
    using N2;

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

sia N1 che N2 contengono un membro Ae perché N3 importa entrambi, il riferimento A in N3 è un errore in fase di compilazione. In questo caso, il conflitto può essere risolto tramite la qualificazione dei riferimenti a Ao introducendo un using_alias_directive che seleziona un particolare Aoggetto . Ad esempio:

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

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

esempio finale

Inoltre, quando più di uno spazio dei nomi o un tipo importato da using_namespace_directiveo using_static_directives nella stessa unità di compilazione o corpo dello spazio dei nomi contengono tipi o membri con lo stesso nome, i riferimenti a tale nome come simple_name sono considerati ambigui.

Esempio:

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 membro Adi tipo e C contiene un campo Astatico e poiché N2 importa entrambi, il riferimento A come simple_name è ambiguo e un errore in fase di compilazione.

esempio finale

Analogamente a un using_alias_directive, un using_namespace_directive non contribuisce ad alcun nuovo membro allo spazio di dichiarazione sottostante dell'unità di compilazione o dello spazio dei nomi, ma influisce solo sull'unità di compilazione o sul corpo dello spazio dei nomi in cui viene visualizzato.

Il namespace_name a cui fa riferimento un using_namespace_directive viene risolto nello stesso modo in cui il namespace_or_type_name a cui fa riferimento un using_alias_directive. Pertanto, using_namespace_directives nella stessa unità di compilazione o corpo dello spazio dei nomi non influiscono l'uno sull'altro e possono essere scritti in qualsiasi ordine.

14.5.4 Uso di direttive statiche

Un using_static_directive importa i tipi annidati e i membri statici contenuti direttamente in una dichiarazione di tipo nell'unità di compilazione o nel corpo dello spazio dei nomi che racchiude immediatamente, consentendo l'identificatore di ogni membro e tipo da usare senza qualifica.

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

All'interno delle dichiarazioni dei membri in un'unità di compilazione o in un corpo dello spazio dei nomi che contiene un using_static_directive, è possibile fare riferimento direttamente ai tipi annidati e ai membri statici accessibili (ad eccezione dei metodi di estensione) contenuti direttamente nella dichiarazione del tipo specificato.

Esempio:

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

Nel codice precedente, all'interno delle dichiarazioni dei membri nello spazio dei N2 nomi, i membri statici e i tipi annidati di N1.A sono direttamente disponibili e pertanto il metodo N è in grado di fare riferimento sia ai B M membri che a N1.A.

esempio finale

Un using_static_directive in particolare non importa i metodi di estensione direttamente come metodi statici, ma li rende disponibili per la chiamata al metodo di estensione (§12.8.9.3).

Esempio:

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

il using_static_directive importa il metodo M di estensione contenuto in N1.A, ma solo come metodo di estensione. Pertanto, il primo riferimento a M nel corpo di restituisce un errore in fase di B.N compilazione perché nessun membro denominato M è nell'ambito.

esempio finale

Un using_static_directive importa solo membri e tipi dichiarati direttamente nel tipo specificato, non membri e tipi dichiarati nelle classi di base.

Esempio:

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

il using_static_directive importa il metodo M2 contenuto in N1.B, ma non importa il metodo M contenuto in N1.A. Di conseguenza, il riferimento a M nel corpo di restituisce un errore in fase di C.N compilazione perché nessun membro denominato M è nell'ambito. Gli sviluppatori devono aggiungere una seconda using static direttiva per specificare che anche i metodi in N1.A devono essere importati.

esempio finale

Le ambiguità tra più using_namespace_directives e using_static_directives sono discusse in §14.5.3.

14.6 Dichiarazioni dei membri dello spazio dei nomi

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

namespace_member_declaration
    : namespace_declaration
    | type_declaration
    ;

Un'unità di compilazione o un corpo dello spazio dei nomi può contenere namespace_member_declarations e tali dichiarazioni contribuiscono nuovi membri allo spazio di dichiarazione sottostante dell'unità di compilazione o del corpo dello spazio dei nomi contenitore.

14.7 Dichiarazioni di tipo

Un type_declaration è 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 può verificarsi come dichiarazione di primo livello in un'unità di compilazione o come dichiarazione membro all'interno di uno spazio dei nomi, una classe o uno struct.

Quando una dichiarazione di tipo per un tipo T si verifica come dichiarazione di primo livello in un'unità di compilazione, il nome completo (§7.8.2) della dichiarazione di tipo è uguale al nome non qualificato della dichiarazione (§7.8.2). Quando una dichiarazione di tipo per un tipo T si verifica all'interno di uno spazio dei nomi, una classe o una dichiarazione di struct, il nome completo (§7.8.3) della dichiarazione di tipo è S.N, dove S è il nome completo dello spazio dei nomi, della classe o della dichiarazione di struct contenitore ed N è il nome non qualificato della dichiarazione.

Un tipo dichiarato all'interno di una classe o uno struct è denominato tipo annidato (§15.3.9).

I modificatori di accesso consentiti e l'accesso predefinito per una dichiarazione di tipo dipendono dal contesto in cui si verifica la dichiarazione (§7.5.2):

  • I tipi dichiarati nelle unità di compilazione o negli spazi dei nomi possono avere public o internal accedere. Il valore predefinito è internal l'accesso.
  • I tipi dichiarati nelle classi possono avere publicaccesso , protectedprotected internal, private protected, , internalo private . Il valore predefinito è private l'accesso.
  • I tipi dichiarati negli struct possono avere publicaccesso , internalo private . Il valore predefinito è private l'accesso.

14.8 Membro alias qualificato

14.8.1 Generale

Il qualificatore :: alias dello spazio dei nomi consente di garantire che le ricerche dei nomi di tipo non siano interessate dall'introduzione di nuovi tipi e membri. Il qualificatore alias dello spazio dei nomi viene sempre visualizzato tra due identificatori denominati identificatori di sinistra e di destra. A differenza del qualificatore regolare . , l'identificatore sinistro del :: qualificatore viene cercato solo come extern o usando alias.

Un qualified_alias_member fornisce l'accesso esplicito allo spazio dei nomi globale e all'extern o all'uso di alias potenzialmente nascosti da altre entità.

qualified_alias_member
    : identifier '::' identifier type_argument_list?
    ;

Un qualified_alias_member può essere utilizzato come namespace_or_type_name (§7.8) o come operando sinistro in un member_access (§12.8.7).

Un qualified_alias_member è costituito da due identificatori, definiti identificatori sinistro e destro, separati dal :: token e, facoltativamente, seguiti da un type_argument_list. Quando l'identificatore a sinistra è globale, viene eseguita la ricerca dell'identificatore di destra nello spazio dei nomi globale. Per qualsiasi altro identificatore sinistro, tale identificatore viene cercato come extern o utilizzando alias (§14.4 e §14.5.2). Si verifica un errore in fase di compilazione se non esiste un alias di questo tipo o se l'alias fa riferimento a un tipo. Se l'alias fa riferimento a uno spazio dei nomi, viene eseguita la ricerca dell'identificatore di destra.

Un qualified_alias_member ha una delle due forme seguenti:

  • N::I<A₁, ..., Aₑ>, dove N e I rappresentano gli identificatori ed <A₁, ..., Aₑ> è un elenco di argomenti di tipo. (e è sempre almeno uno.
  • N::I, dove N e I rappresentano gli identificatori. In questo caso, e è considerato zero.

Usando questa notazione, il significato di un qualified_alias_member viene determinato come segue:

  • Se N è l'identificatore global, viene eseguita la ricerca dello spazio dei nomi globale per I:
    • Se lo spazio dei nomi globale contiene uno spazio dei nomi denominato I e e è zero, il qualified_alias_member fa riferimento a tale spazio dei nomi.
    • In caso contrario, se lo spazio dei nomi globale contiene un tipo non generico denominato I e e è zero, il qualified_alias_member fa riferimento a tale tipo.
    • In caso contrario, se lo spazio dei nomi globale contiene un tipo denominato I con e parametri di tipo, il qualified_alias_member fa riferimento a quel tipo costruito con gli argomenti di tipo specificati.
    • In caso contrario, il qualified_alias_member non è definito e si verifica un errore in fase di compilazione.
  • In caso contrario, a partire dalla dichiarazione dello spazio dei nomi (§14.3) immediatamente contenente il qualified_alias_member (se presente), continuando con ogni dichiarazione dello spazio dei nomi racchiuso (se presente) e terminando con l'unità di compilazione contenente il qualified_alias_member, i passaggi seguenti vengono valutati fino a quando non si trova un'entità:
    • Se la dichiarazione dello spazio dei nomi o l'unità di compilazione contiene un using_alias_directive che associa N a un tipo, il qualified_alias_member non è definito e si verifica un errore in fase di compilazione.
    • In caso contrario, se la dichiarazione dello spazio dei nomi o l'unità di compilazione contiene un extern_alias_directive o un using_alias_directive che associa N a uno spazio dei nomi, allora:
      • Se lo spazio dei nomi associato a N contiene uno spazio dei nomi denominato I e e è zero, il qualified_alias_member fa riferimento a tale spazio dei nomi.
      • In caso contrario, se lo spazio dei nomi associato a N contiene un tipo non generico denominato I e e è zero, il qualified_alias_member fa riferimento a tale tipo.
      • In caso contrario, se lo spazio dei nomi associato a N contiene un tipo denominato I con e parametri di tipo, il qualified_alias_member fa riferimento a quel tipo costruito con gli argomenti di tipo specificati.
      • In caso contrario, il qualified_alias_member non è definito e si verifica un errore in fase di compilazione.
  • In caso contrario, il qualified_alias_member non è definito e si verifica un errore in fase di compilazione.

Esempio: nel codice:

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

viene fatto riferimento alla classe A con global::A e il tipo System.Net.Sockets.Socket viene fatto riferimento a S::Socket. L'uso A.x di e S.Socket avrebbe invece causato errori in fase di compilazione perché A e S sarebbero stati risolti nei parametri.

esempio finale

Nota: l'identificatore global ha un significato speciale solo quando viene usato come identificatore sinistro di un qualified_alias_name. Non è una parola chiave e non è un alias; è una parola chiave contestuale (§6.4.4). Nel codice:

class A { }

class C
{
    global.A x; // Error: global is not defined
    global::A y; // Valid: References A in the global namespace
}

l'uso global.A di genera un errore in fase di compilazione perché non è presente alcuna entità denominata global nell'ambito. Se un'entità denominata global fosse nell'ambito, global in global.A sarebbe stata risolta in tale entità.

L'uso global come identificatore sinistro di un qualified_alias_member genera sempre una ricerca nello spazio dei global nomi, anche se è presente un alias denominato global. Nel codice:

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 viene risolto in MyGlobalTypes.A e global::A viene risolto nella classe A nello spazio dei nomi globale.

nota finale

14.8.2 Univocità degli alias

Ogni unità di compilazione e il corpo dello spazio dei nomi hanno uno spazio di dichiarazione separato per gli alias extern e l'uso di alias. Pertanto, mentre il nome di un alias extern o l'uso di alias deve essere univoco all'interno del set di alias extern e usando alias dichiarati nel corpo immediatamente contenente l'unità di compilazione o lo spazio dei nomi, è consentito che un alias abbia lo stesso nome di un tipo o di uno spazio dei nomi purché venga usato solo con il :: qualificatore.

Esempio: nell'esempio seguente:

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

il nome A ha due significati possibili nel secondo corpo dello spazio dei nomi perché sia la classe A che l'alias A using sono nell'ambito. Per questo motivo, l'uso di A nel nome A.Stream completo è ambiguo e causa un errore in fase di compilazione. Tuttavia, l'uso di A con il :: qualificatore non è un errore perché A viene cercato solo come alias dello spazio dei nomi.

esempio finale