Udostępnij za pośrednictwem


14 przestrzeni nazw

14.1 Ogólne

Programy języka C# są zorganizowane przy użyciu przestrzeni nazw. Przestrzenie nazw są używane zarówno jako "wewnętrzny" system organizacji dla programu, jak i jako "zewnętrzny" system organizacji — sposób prezentowania elementów programu, które są widoczne dla innych programów.

Używanie dyrektyw (§14.5) jest udostępniane w celu ułatwienia korzystania z przestrzeni nazw.

14.2 Jednostki kompilacji

Compilation_unit składa się z zera lub większej liczby extern_alias_directive, po których następuje zero lub więcej using_directive, po których następuje zero lub jeden global_attributes, po którym następuje zero lub więcej namespace_member_declaration s. Compilation_unit definiuje ogólną strukturę danych wejściowych.

compilation_unit
    : extern_alias_directive* using_directive* global_attributes?
      namespace_member_declaration*
    ;

Program w języku C# składa się z co najmniej jednej jednostki kompilacji. Po skompilowaniu programu w języku C# wszystkie jednostki kompilacji są przetwarzane razem. W związku z tym jednostki kompilacji mogą zależeć od siebie, prawdopodobnie w sposób okrągły.

Extern_alias_directive s jednostki kompilacji mają wpływ na using_directives, global_attributes i namespace_member_declarations tej jednostki kompilacji, ale nie mają wpływu na inne jednostki kompilacji.

Using_directive jednostki kompilacji mają wpływ na global_attributes i namespace_member_declarationtej jednostki kompilacji, ale nie mają wpływu na inne jednostki kompilacji.

Global_attributes (§22.3) jednostki kompilacji zezwalają na specyfikację atrybutów dla docelowego zestawu i modułu. Zestawy i moduły działają jako kontenery fizyczne dla typów. Zestaw może składać się z kilku fizycznie oddzielnych modułów.

Każda jednostka kompilacji programu namespace_member_declarationwspółtworzy elementy członkowskie do pojedynczej przestrzeni deklaracji nazywanej globalną przestrzenią nazw.

Przykład:

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

Dwie jednostki kompilacji przyczyniają się do pojedynczej globalnej przestrzeni nazw, w tym przypadku deklarując dwie klasy z w pełni kwalifikowanymi nazwami A i B. Ponieważ dwie jednostki kompilacji przyczyniają się do tej samej przestrzeni deklaracji, byłby to błąd, gdyby każda zawierała deklarację elementu członkowskiego o tej samej nazwie.

przykład końcowy

14.3 Deklaracje przestrzeni nazw

Namespace_declaration składa się z przestrzeni nazw słów kluczowych, po której następuje nazwa i treść przestrzeni nazw, a następnie średnik.

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

qualified_identifier
    : identifier ('.' identifier)*
    ;

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

Namespace_declaration może wystąpić jako deklaracja najwyższego poziomu w compilation_unit lub jako deklaracja elementu członkowskiego w ramach innego namespace_declaration. Gdy namespace_declaration występuje jako deklaracja najwyższego poziomu w compilation_unit, przestrzeń nazw staje się członkiem globalnej przestrzeni nazw. Gdy namespace_declaration występuje w innym namespace_declaration, wewnętrzna przestrzeń nazw staje się członkiem zewnętrznej przestrzeni nazw. W obu przypadkach nazwa przestrzeni nazw jest unikatowa w obrębie zawierającej przestrzeni nazw.

Przestrzenie nazw są niejawnie public i deklaracja przestrzeni nazw nie może zawierać żadnych modyfikatorów dostępu.

W ramach namespace_body opcjonalne using_directiveimportować nazwy innych przestrzeni nazw, typów i elementów członkowskich, co pozwala na bezpośrednie odwołowanie się do nich zamiast za pomocą kwalifikowanych nazw. Opcjonalne namespace_member_declarationwspółtworzenie elementów członkowskich do przestrzeni nazw deklaracji. Należy pamiętać, że wszystkie using_directivepojawiają się przed wszelkimi deklaracjami członków.

Qualified_identifier namespace_declaration może być pojedynczym identyfikatorem lub sekwencją identyfikatorów oddzielonych tokenami "".. Ta ostatnia forma umożliwia programowi zdefiniowanie zagnieżdżonej przestrzeni nazw bez zagnieżdżania kilku deklaracji przestrzeni nazw.

Przykład:

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

jest semantycznie równoważne

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

przykład końcowy

Przestrzenie nazw są otwarte, a dwie deklaracje przestrzeni nazw o tej samej w pełni kwalifikowanej nazwie (§7.8.2) przyczyniają się do tej samej przestrzeni deklaracji (§7.3).

Przykład: w poniższym kodzie

namespace N1.N2
{
    class A {}
}

namespace N1.N2
{
    class B {}
}

dwa powyższe deklaracje przestrzeni nazw przyczyniają się do tej samej przestrzeni deklaracji, w tym przypadku deklarując dwie klasy z w pełni kwalifikowanymi nazwami N1.N2.A i N1.N2.B. Ponieważ dwie deklaracje przyczyniają się do tej samej przestrzeni deklaracji, byłby to błąd, gdyby każda zawierała deklarację elementu członkowskiego o tej samej nazwie.

przykład końcowy

14.4 Dyrektywy aliasu extern

Extern_alias_directive wprowadza identyfikator, który służy jako alias dla przestrzeni nazw. Specyfikacja aliasowanej przestrzeni nazw jest zewnętrzna dla kodu źródłowego programu i ma zastosowanie również do zagnieżdżonych przestrzeni nazw aliasów przestrzeni nazw.

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

Zakres extern_alias_directive rozciąga się na using_directives, global_attributes i namespace_member_declarations jego natychmiast zawierających compilation_unit lub namespace_body.

W ramach jednostki kompilacji lub treści przestrzeni nazw zawierającej extern_alias_directive identyfikator wprowadzony przez extern_alias_directive może służyć do odwołowania się do aliasowanej przestrzeni nazw. Jest to błąd czasu kompilacji dla identyfikatora jako słowa global.

Alias wprowadzony przez extern_alias_directive jest bardzo podobny do aliasu wprowadzonego przez using_alias_directive. Zobacz §14.5.2 , aby uzyskać bardziej szczegółowe omówienie extern_alias_directives i using_alias_directives.

alias jest kontekstowym słowem kluczowym (§6.4.4) i ma specjalne znaczenie tylko wtedy, gdy natychmiast następuje słowo extern kluczowe w extern_alias_directive.

Błąd występuje, jeśli program deklaruje alias extern, dla którego nie podano definicji zewnętrznej.

Przykład: Następujący program deklaruje i używa dwóch aliasów eksternowych i X Y, z których każdy reprezentuje katalog główny odrębnej hierarchii przestrzeni nazw:

extern alias X;
extern alias Y;

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

Program deklaruje istnienie aliasów X extern i Y, ale rzeczywiste definicje aliasów są zewnętrzne dla programu. Klasy o identycznych nazwach N.B można teraz przywoływać jako X.N.B i Y.N.B, lub przy użyciu kwalifikatora X::N.B aliasu przestrzeni nazw i Y::N.B. przykład końcowy

14.5 Używanie dyrektyw

14.5.1 Ogólne

Używanie dyrektyw ułatwia korzystanie z przestrzeni nazw i typów zdefiniowanych w innych przestrzeniach nazw. Używanie dyrektyw ma wpływ na proces rozpoznawania nazw namespace_or_type_names (§7.8) i simple_names (§12.8.4), ale w przeciwieństwie do deklaracji, using_directives nie przyczyniają nowych elementów członkowskich do podstawowych przestrzeni deklaracji jednostek kompilacji lub przestrzeni nazw, w których są używane.

using_directive
    : using_alias_directive
    | using_namespace_directive
    | using_static_directive    
    ;

Using_alias_directive (§14.5.2) wprowadza alias dla przestrzeni nazw lub typu.

Using_namespace_directive (§14.5.3) importuje elementy członkowskie typu przestrzeni nazw.

Using_static_directive (§14.5.4) importuje zagnieżdżone typy i statyczne elementy członkowskie typu.

Zakres using_directive rozciąga się na namespace_member_declarations jego natychmiast zawierającej jednostkę kompilacji lub treść przestrzeni nazw. Zakres using_directive nie obejmuje using_directive równorzędnych s. W związku z tym równorzędne using_directive nie wpływają na siebie, a kolejność, w jakiej są zapisywane, jest nieistotna. Natomiast zakres extern_alias_directive obejmuje using_directivezdefiniowane w tej samej jednostce kompilacji lub treści przestrzeni nazw.

14.5.2 Używanie dyrektyw aliasu

Using_alias_directive wprowadza identyfikator, który służy jako alias przestrzeni nazw lub typu w obrębie natychmiast otaczającej jednostki kompilacji lub treści przestrzeni nazw.

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

W ramach atrybutów globalnych i deklaracji składowych w jednostce kompilacji lub treści przestrzeni nazw, która zawiera using_alias_directive, identyfikator wprowadzony przez using_alias_directive może służyć do odwoływanie się do danej przestrzeni nazw lub typu.

Przykład:

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

    class B: A {}
}

Powyżej, w deklaracjach składowych w N3 przestrzeni nazw, A jest aliasem dla N1.N2.Aklasy , a tym samym klasa N3.B pochodzi z klasy N1.N2.A. Ten sam efekt można uzyskać, tworząc alias R dla elementu N1.N2 , a następnie odwołując R.Asię do elementu :

namespace N3
{
    using R = N1.N2;

    class B : R.A {}
}

przykład końcowy

W ramach dyrektyw using atrybuty globalne i deklaracje składowe w jednostce kompilacji lub treści przestrzeni nazw, która zawiera extern_alias_directive, identyfikator wprowadzony przez extern_alias_directive może służyć do odwoływanie się do skojarzonej przestrzeni nazw.

Przykład: Na przykład:

namespace N1
{
    extern alias N2;

    class B : N2::A {}
}

Powyżej, w deklaracjach składowych w N1 przestrzeni nazw, jest aliasem dla niektórych przestrzeni nazw, N2 których definicja jest zewnętrzna dla kodu źródłowego programu. Klasa N1.B pochodzi z klasy N2.A. Ten sam efekt można uzyskać, tworząc alias A dla elementu N2.A , a następnie odwołując Asię do elementu :

namespace N1
{
    extern alias N2;

    using A = N2::A;

    class B : A {}
}

przykład końcowy

Extern_alias_directive lub using_alias_directive udostępnia alias w ramach konkretnej jednostki kompilacji lub treści przestrzeni nazw, ale nie współtworzy żadnych nowych elementów członkowskich do podstawowej przestrzeni deklaracji. Innymi słowy, dyrektywa aliasu nie jest przechodnia, ale raczej wpływa tylko na jednostkę kompilacji lub treść przestrzeni nazw, w której występuje.

Przykład: w poniższym kodzie

namespace N3
{
    extern alias R1;

    using R2 = N1.N2;
}

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

zakresy dyrektyw aliasu, które wprowadzają R1 i R2 rozszerzają się tylko na deklaracje składowe w treści przestrzeni nazw, w której są zawarte, więc R1 i R2 są nieznane w drugiej deklaracji przestrzeni nazw. Jednak umieszczenie dyrektyw aliasu w jednostce kompilacji zawierającej powoduje udostępnienie aliasu w obu deklaracjach przestrzeni nazw:

extern alias R1;

using R2 = N1.N2;

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

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

przykład końcowy

Każda extern_alias_directive lub using_alias_directive w compilation_unit lub namespace_body przyczynia się do nazwy przestrzeni deklaracji aliasu (§7.3) natychmiast otaczającej compilation_unit lub namespace_body. Identyfikator dyrektywy aliasu jest unikatowy w odpowiedniej przestrzeni deklaracji aliasu. Identyfikator aliasu nie musi być unikatowy w przestrzeni deklaracji globalnej ani przestrzeni deklaracji odpowiadającej jej przestrzeni nazw.

Przykład:

extern alias X;
extern alias Y;

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

class Y {} // Ok

Alias using o nazwie X powoduje błąd, ponieważ w tej samej lekcji kompilacji istnieje już alias o nazwie X . Klasa o nazwie Y nie powoduje konfliktu z aliasem extern nazwanym Y , ponieważ te nazwy są dodawane do odrębnych spacji deklaracji. Pierwszy element jest dodawany do przestrzeni deklaracji globalnej, a drugi jest dodawany do przestrzeni deklaracji aliasu dla tej lekcji kompilacji.

Gdy nazwa aliasu jest zgodna z nazwą członka przestrzeni nazw, użycie jednej z tych nazw jest odpowiednio kwalifikowane:

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
}

W drugiej treści przestrzeni nazw dla N3programu niekwalifikowane użycie B wyników powoduje wystąpienie błędu, ponieważ N3 zawiera element członkowski o nazwie B i treść przestrzeni nazw, która deklaruje również alias o nazwie B; podobnie dla Aelementu . N3.B Klasę można przywoływać jako N3.B lub global::N3.B. Alias A może być używany w kwalifikowanym aliasie członkowskim (§14.8), takim jak A::B. Alias jest zasadniczo bezużyteczny B . Nie można jej używać w qualified_alias_member , ponieważ w qualified_alias_member i B aliasach można używać tylko aliasów przestrzeni nazw.

przykład końcowy

Podobnie jak zwykłe elementy członkowskie, nazwy wprowadzone przez alias_directives są ukryte przez podobnie nazwane elementy członkowskie w zagnieżdżonych zakresach.

Przykład: w poniższym kodzie

using R = N1.N2;

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

odwołanie do R.A w deklaracji powoduje B błąd czasu kompilacji, ponieważ R odnosi się do N3.R, a nie N1.N2.

przykład końcowy

Kolejność zapisywania extern_alias_directivenie ma znaczenia. Podobnie kolejność zapisu using_alias_directives nie ma znaczenia, ale wszystkie using_alias_directives pochodzą po wszystkich extern_alias_directive s w tej samej jednostce kompilacji lub w treści przestrzeni nazw. Rozwiązanie namespace_or_type_name, do którego odwołuje się using_alias_directive, nie ma wpływu na samą using_alias_directive lub przez inne using_directives w natychmiast zawierającej jednostkę kompilacji lub treść przestrzeni nazw, ale może to mieć wpływ na extern_alias_directivew natychmiast zawierającej jednostkę kompilacji lub treść przestrzeni nazw. Innymi słowy, namespace_or_type_name using_alias_directive jest rozpoznawana tak, jakby natychmiast zawierająca jednostkę kompilacji lub treść przestrzeni nazw nie miała using_directive s, ale ma poprawny zestaw extern_alias_directives.

Przykład: w poniższym kodzie

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
}

ostatni using_alias_directive powoduje wystąpienie błędu czasu kompilacji, ponieważ nie ma to wpływu na poprzednią using_alias_directive. Pierwszy using_alias_directive nie powoduje błędu, ponieważ zakres aliasu extern X obejmuje using_alias_directive.

przykład końcowy

Using_alias_directive może utworzyć alias dla dowolnej przestrzeni nazw lub typu, w tym przestrzeni nazw, w której się pojawia, oraz dowolnej przestrzeni nazw lub typu zagnieżdżonego w tej przestrzeni nazw.

Uzyskiwanie dostępu do przestrzeni nazw lub typu za pośrednictwem aliasu daje dokładnie taki sam wynik, jak uzyskiwanie dostępu do tej przestrzeni nazw lub typu za pośrednictwem zadeklarowanej nazwy.

Przykład: podane

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

nazwy N1.N2.A, R1.N2.Ai R2.A są równoważne i wszystkie odnoszą się do deklaracji klasy, której w pełni kwalifikowana nazwa to N1.N2.A.

przykład końcowy

Chociaż każda część częściowego typu (§15.2.7) jest zadeklarowana w tej samej przestrzeni nazw, części są zwykle zapisywane w różnych deklaracjach przestrzeni nazw. W związku z tym różne extern_alias_directives i using_directivemogą być obecne dla każdej części. Podczas interpretowania prostych nazw (§12.8.4) w jednej części uwzględniane są tylko extern_alias_directives i using_directivejednostki przestrzeni nazw i jednostki kompilacji otaczającej tę część. Może to spowodować, że ten sam identyfikator ma różne znaczenie w różnych częściach.

Przykład:

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

przykład końcowy

Użycie aliasów może nazwać zamknięty typ skonstruowany, ale nie może nazwać niezwiązanej deklaracji typu ogólnego bez podawania argumentów typu.

Przykład:

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
}

przykład końcowy

14.5.3 Używanie dyrektyw przestrzeni nazw

Using_namespace_directive importuje typy zawarte w przestrzeni nazw do natychmiast otaczającej jednostki kompilacji lub treści przestrzeni nazw, umożliwiając korzystanie z identyfikatora każdego typu bez kwalifikacji.

using_namespace_directive
    : 'using' namespace_name ';'
    ;

W deklaracjach składowych w jednostce kompilacji lub w treści przestrzeni nazw zawierającej using_namespace_directive można odwoływać się bezpośrednio do typów zawartych w danej przestrzeni nazw.

Przykład:

namespace N1.N2
{
    class A {}
}

namespace N3
{
    using N1.N2;

    class B : A {}
}

Powyżej, w deklaracjach składowych w N3 przestrzeni nazw, składowe N1.N2 typu są dostępne bezpośrednio, a tym samym klasy N3.B pochodzą z klasy N1.N2.A.

przykład końcowy

Using_namespace_directive importuje typy zawarte w danej przestrzeni nazw, ale w szczególności nie importuje zagnieżdżonych przestrzeni nazw.

Przykład: w poniższym kodzie

namespace N1.N2
{
    class A {}
}

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

using_namespace_directive importuje typy zawarte w N1pliku , ale nie przestrzenie nazw zagnieżdżone w pliku N1. W związku z tym odwołanie do N2.A w deklaracji B wyników powoduje błąd czasu kompilacji, ponieważ żadne elementy członkowskie o nazwie N2 nie są w zakresie.

przykład końcowy

W przeciwieństwie do using_alias_directive using_namespace_directive może importować typy, których identyfikatory są już zdefiniowane w otaczającej jednostce kompilacji lub w treści przestrzeni nazw. W efekcie nazwy importowane przez using_namespace_directive są ukryte przez podobnie nazwane elementy członkowskie w otaczającej jednostce kompilacji lub treści przestrzeni nazw.

Przykład:

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

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

W tym miejscu, w deklaracjach składowych w N3 przestrzeni nazw, A odnosi się do N3.A zamiast N1.N2.A.

przykład końcowy

Ponieważ nazwy mogą być niejednoznaczne, gdy więcej niż jedna zaimportowana przestrzeń nazw wprowadza taką samą nazwę typu, using_alias_directive przydaje się do uściślania odwołania.

Przykład: w poniższym kodzie

namespace N1
{
    class A {}
}

namespace N2
{
    class A {}
}

namespace N3
{
    using N1;
    using N2;

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

zarówno N1 element członkowskiA, jak i N2 zawiera element , a ponieważ N3 importuje oba, odwoływanie się w elemencie A N3 jest błędem czasu kompilacji. W takiej sytuacji konflikt można rozwiązać za pomocą kwalifikacji odwołań do A, lub wprowadzając using_alias_directive , który wybiera określony Aelement . Na przykład:

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

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

przykład końcowy

Ponadto jeśli więcej niż jedna przestrzeń nazw lub typ importowany przez using_namespace_directives lub using_static_directivew tej samej jednostce kompilacji lub treści przestrzeni nazw zawierają typy lub elementy członkowskie o tej samej nazwie, odwołania do tej nazwy jako simple_name są uważane za niejednoznaczne.

Przykład:

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 zawiera element członkowski Atypu i C zawiera pole Astatyczne , a ponieważ N2 importuje oba, odwołujące się A do simple_name jest niejednoznaczne i błąd czasu kompilacji.

przykład końcowy

Podobnie jak using_alias_directive, using_namespace_directive nie przyczynia się do żadnych nowych elementów członkowskich podstawowej przestrzeni deklaracji jednostki kompilacji lub przestrzeni nazw, ale raczej wpływa tylko na jednostkę kompilacji lub treść przestrzeni nazw, w której się pojawia.

Namespace_name, do których odwołuje się using_namespace_directive, jest rozpoznawana w taki sam sposób, jak namespace_or_type_name, do których odwołuje się using_alias_directive. W związku z tym using_namespace_directivew tej samej jednostce kompilacji lub treści przestrzeni nazw nie wpływają na siebie i mogą być zapisywane w dowolnej kolejności.

14.5.4 Używanie dyrektyw statycznych

Using_static_directive importuje zagnieżdżone typy i statyczne elementy członkowskie zawarte bezpośrednio w deklaracji typu do bezpośrednio otaczającej jednostki kompilacji lub treści przestrzeni nazw, umożliwiając korzystanie z identyfikatora każdego elementu członkowskiego i typu bez kwalifikacji.

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

W deklaracjach składowych w jednostce kompilacji lub treści przestrzeni nazw, która zawiera using_static_directive, dostępne typy zagnieżdżone i statyczne elementy członkowskie (z wyjątkiem metod rozszerzeń) zawarte bezpośrednio w deklaracji danego typu można odwoływać się bezpośrednio.

Przykład:

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

W poprzednim kodzie, w deklaracjach składowych w N2 przestrzeni nazw statyczne elementy członkowskie i zagnieżdżone typy N1.A są dostępne bezpośrednio, a zatem metoda N może odwoływać się zarówno B do elementów N1.Aczłonkowskich , jak i M .

przykład końcowy

Using_static_directive nie importuje metod rozszerzeń bezpośrednio jako metod statycznych, ale udostępnia je dla wywołania metody rozszerzenia (§12.8.9.3).

Przykład:

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

using_static_directive importuje metodę M rozszerzenia zawartą w N1.Ametodzie , ale tylko jako metodę rozszerzenia. W związku z tym pierwsze odwołanie do M elementu w treści B.N wyników powoduje błąd czasu kompilacji, ponieważ żadne elementy członkowskie o nazwie M nie są w zakresie.

przykład końcowy

Using_static_directive importuje tylko składowe i typy zadeklarowane bezpośrednio w danym typie, a nie składowe i typy zadeklarowane w klasach bazowych.

Przykład:

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

using_static_directive importuje metodę M2 zawartą w N1.Bpliku , ale nie importuje metody M zawartej w N1.Apliku . W związku z tym odwołanie do M elementu w treści C.N wyników jest błędem czasu kompilacji, ponieważ żadne elementy członkowskie o nazwie M nie są w zakresie. Deweloperzy muszą dodać drugą using static dyrektywę, aby określić, że metody w N1.A programie powinny być również importowane.

przykład końcowy

Niejednoznaczności między wieloma using_namespace_directives i using_static_directives są omawiane w §14.5.3.

14.6 Deklaracje składowych przestrzeni nazw

Namespace_member_declaration jest namespace_declaration (§14.3) lub type_declaration (§14.7).

namespace_member_declaration
    : namespace_declaration
    | type_declaration
    ;

Jednostka kompilacji lub treść przestrzeni nazw może zawierać namespace_member_declarations, a takie deklaracje przyczyniają się do przestrzeni deklaracji bazowej jednostki kompilacji lub treści przestrzeni nazw.

14.7 Deklaracje typów

Type_declaration jest class_declaration (§15.2), struct_declaration (§16.2), interface_declaration (§18.2), enum_declaration (§19.2) lub delegate_declaration (§20.2).

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

Type_declaration może wystąpić jako deklaracja najwyższego poziomu w jednostce kompilacji lub jako deklaracja składowa w przestrzeni nazw, klasie lub struktury.

Gdy deklaracja typu dla typu T występuje jako deklaracja najwyższego poziomu w jednostce kompilacji, w pełni kwalifikowana nazwa (§7.8.2) deklaracji typu jest taka sama jak niekwalifikowana nazwa deklaracji (§7.8.2). Gdy deklaracja typu dla typu T występuje w przestrzeni nazw, klasie lub deklaracji struktury, w pełni kwalifikowana nazwa (§7.8.3) deklaracji typu , gdzie S jest w pełni kwalifikowaną nazwą zawierającej przestrzeń nazw, klasę lub deklarację struktury i N jest niekwalifikowaną nazwą deklaracji S.Ndeklaracji.

Typ zadeklarowany w klasie lub strukturę jest nazywany typem zagnieżdżonym (§15.3.9).

Dozwolone modyfikatory dostępu i domyślny dostęp do deklaracji typu zależą od kontekstu, w którym odbywa się deklaracja (§7.5.2):

  • Typy zadeklarowane w jednostkach kompilacji lub przestrzeniach nazw mogą mieć public dostęp lub internal . Wartość domyślna to internal dostęp.
  • Typy zadeklarowane w klasach mogą mieć publicdostęp , , protected internalprotected, private protected, internallub private . Wartość domyślna to private dostęp.
  • Typy zadeklarowane w strukturach mogą mieć publicdostęp , internallub private . Wartość domyślna to private dostęp.

14.8 Kwalifikowany element członkowski aliasu

14.8.1 Ogólne

Kwalifikator :: aliasu przestrzeni nazw umożliwia zagwarantowanie, że wyszukiwanie nazw typów nie ma wpływu na wprowadzenie nowych typów i elementów członkowskich. Kwalifikator aliasu przestrzeni nazw zawsze pojawia się między dwoma identyfikatorami nazywanymi identyfikatorami po lewej i prawej stronie. W przeciwieństwie do zwykłego . :: kwalifikatora, lewy identyfikator kwalifikatora jest sprawdzany tylko jako extern lub używając aliasu.

Qualified_alias_member zapewnia jawny dostęp do globalnej przestrzeni nazw oraz do eksternowania lub używania aliasów, które są potencjalnie ukryte przez inne jednostki.

qualified_alias_member
    : identifier '::' identifier type_argument_list?
    ;

Qualified_alias_member można użyć jako namespace_or_type_name (§7.8) lub jako lewy operand w member_access (§12.8.7).

Qualified_alias_member składa się z dwóch identyfikatorów, nazywanych identyfikatorami po lewej i prawej stronie, oddzielonymi tokenem :: i opcjonalnie po type_argument_list. Gdy identyfikator po lewej stronie jest globalny, globalna przestrzeń nazw jest wyszukiwana pod kątem identyfikatora po prawej stronie. W przypadku każdego innego identyfikatora po lewej stronie ten identyfikator jest sprawdzany jako extern lub przy użyciu aliasu (§14.4 i §14.5.2). Błąd czasu kompilacji występuje, jeśli nie ma takiego aliasu lub alias odwołuje się do typu. Jeśli alias odwołuje się do przestrzeni nazw, ta przestrzeń nazw jest wyszukiwana pod kątem identyfikatora po prawej stronie.

Qualified_alias_member ma jedną z dwóch form:

  • N::I<A₁, ..., Aₑ>, gdzie N i I reprezentują identyfikatory, i <A₁, ..., Aₑ> jest listą argumentów typu. (e jest zawsze co najmniej jeden).
  • N::I, gdzie N i I reprezentują identyfikatory. (W tym przypadku e jest uważany za zero).

Korzystając z tej notacji, znaczenie qualified_alias_member jest określane w następujący sposób:

  • Jeśli N jest identyfikatorem global, wyszukiwana Ijest globalna przestrzeń nazw:
    • Jeśli globalna przestrzeń nazw zawiera przestrzeń nazw o nazwie I i e ma wartość zero, qualified_alias_member odnosi się do tej przestrzeni nazw.
    • W przeciwnym razie, jeśli globalna przestrzeń nazw zawiera typ niegeneryczny o nazwie I i e ma wartość zero, qualified_alias_member odnosi się do tego typu.
    • W przeciwnym razie, jeśli globalna przestrzeń nazw zawiera typ o nazwie I , który ma e parametry typu, qualified_alias_member odnosi się do tego typu skonstruowanego przy użyciu podanych argumentów typu.
    • W przeciwnym razie qualified_alias_member jest niezdefiniowany i występuje błąd czasu kompilacji.
  • W przeciwnym razie, począwszy od deklaracji przestrzeni nazw (§14.3) natychmiast zawierającej qualified_alias_member (jeśli istnieje), kontynuując każdą otaczającą deklarację przestrzeni nazw (jeśli istnieje), a kończąc na jednostce kompilacji zawierającej qualified_alias_member, następujące kroki są oceniane do momentu zlokalizowania jednostki:
    • Jeśli deklaracja lub jednostka kompilacji przestrzeni nazw zawiera using_alias_directive , który kojarzy N z typem, qualified_alias_member jest niezdefiniowany i występuje błąd czasu kompilacji.
    • W przeciwnym razie, jeśli deklaracja przestrzeni nazw lub jednostka kompilacji zawiera extern_alias_directive lub using_alias_directive , które są skojarzone z przestrzenią nazw, wówczas N :
      • Jeśli przestrzeń nazw skojarzona z N przestrzenią nazw o nazwie I i e ma wartość zero, qualified_alias_member odnosi się do tej przestrzeni nazw.
      • W przeciwnym razie, jeśli przestrzeń nazw skojarzona z elementem N zawiera typ niegeneryczny o nazwie I i e ma wartość zero, qualified_alias_member odwołuje się do tego typu.
      • W przeciwnym razie, jeśli przestrzeń nazw skojarzona z elementem N zawiera typ o nazwie I , który ma e parametry typu, qualified_alias_member odnosi się do tego typu skonstruowanego z podanymi argumentami typu.
      • W przeciwnym razie qualified_alias_member jest niezdefiniowany i występuje błąd czasu kompilacji.
  • W przeciwnym razie qualified_alias_member jest niezdefiniowany i występuje błąd czasu kompilacji.

Przykład: w kodzie:

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

klasa A jest przywołynięta global::A , a typ System.Net.Sockets.Socket jest przywołyyny za pomocą S::Socketpolecenia . Użycie elementu A.x i S.Socket zamiast tego spowodowałoby błędy czasu kompilacji, ponieważ A parametry zostałyby rozwiązane.S

przykład końcowy

Uwaga: identyfikator global ma specjalne znaczenie tylko wtedy, gdy jest używany jako identyfikator po lewej stronie qualified_alias_name. Nie jest to słowo kluczowe i nie jest to alias; jest to słowo kluczowe kontekstowe (§6.4.4). W kodzie:

class A { }

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

użycie global.A powoduje błąd czasu kompilacji, ponieważ nie ma jednostki o nazwie global w zakresie. Jeśli niektóre jednostki o nazwie global były w zakresie, wówczas global global.A w poleceniu zostałaby rozpoznana dla tej jednostki.

Użycie global jako identyfikatora po lewej stronie qualified_alias_member zawsze powoduje wyszukiwanie w global przestrzeni nazw, nawet jeśli istnieje alias o nazwie global. W kodzie:

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.AMyGlobalTypes.A rozpoznaje i global::A rozpoznaje klasę A w globalnej przestrzeni nazw.

notatka końcowa

14.8.2 Unikatowość aliasów

Każda jednostka kompilacji i treść przestrzeni nazw ma oddzielną przestrzeń deklaracji dla aliasów extern i używa aliasów. W związku z tym, podczas gdy nazwa aliasu extern lub używanie aliasu jest unikatowa w zestawie aliasów extern i przy użyciu aliasów zadeklarowanych w natychmiast zawierającej jednostkę kompilacji lub treść przestrzeni nazw, alias może mieć taką samą nazwę jak typ lub przestrzeń nazw, o ile jest używany tylko z kwalifikatorem :: .

Przykład: w następujących kwestiach:

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

nazwa A ma dwa możliwe znaczenia w drugiej treści przestrzeni nazw, ponieważ zarówno klasa A , jak i alias A using są w zakresie. Z tego powodu użycie elementu A w kwalifikowanej nazwie A.Stream jest niejednoznaczne i powoduje wystąpienie błędu czasu kompilacji. Jednak użycie elementu A z :: kwalifikatorem nie jest błędem, ponieważ A jest ona sprawdzana tylko jako alias przestrzeni nazw.

przykład końcowy