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
iB
. 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
iN1.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 iY
, ale rzeczywiste definicje aliasów są zewnętrzne dla programu. Klasy o identycznych nazwachN.B
można teraz przywoływać jakoX.N.B
iY.N.B
, lub przy użyciu kwalifikatoraX::N.B
aliasu przestrzeni nazw iY::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 dlaN1.N2.A
klasy , a tym samym klasaN3.B
pochodzi z klasyN1.N2.A
. Ten sam efekt można uzyskać, tworząc aliasR
dla elementuN1.N2
, a następnie odwołującR.A
się 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. KlasaN1.B
pochodzi z klasyN2.A
. Ten sam efekt można uzyskać, tworząc aliasA
dla elementuN2.A
, a następnie odwołującA
się 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
iR2
rozszerzają się tylko na deklaracje składowe w treści przestrzeni nazw, w której są zawarte, więcR1
iR2
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 nazwieX
. Klasa o nazwieY
nie powoduje konfliktu z aliasem extern nazwanymY
, 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
N3
programu niekwalifikowane użycieB
wyników powoduje wystąpienie błędu, ponieważN3
zawiera element członkowski o nazwieB
i treść przestrzeni nazw, która deklaruje również alias o nazwieB
; podobnie dlaA
elementu .N3.B
Klasę można przywoływać jakoN3.B
lubglobal::N3.B
. AliasA
może być używany w kwalifikowanym aliasie członkowskim (§14.8), takim jakA::B
. Alias jest zasadniczo bezużytecznyB
. Nie można jej używać w qualified_alias_member , ponieważ w qualified_alias_member iB
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 powodujeB
błąd czasu kompilacji, ponieważR
odnosi się doN3.R
, a nieN1.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.A
iR2.A
są równoważne i wszystkie odnoszą się do deklaracji klasy, której w pełni kwalifikowana nazwa toN1.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ładoweN1.N2
typu są dostępne bezpośrednio, a tym samym klasyN3.B
pochodzą z klasyN1.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
N1
pliku , ale nie przestrzenie nazw zagnieżdżone w plikuN1
. W związku z tym odwołanie doN2.A
w deklaracjiB
wyników powoduje błąd czasu kompilacji, ponieważ żadne elementy członkowskie o nazwieN2
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ę doN3.A
zamiastN1.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 iN2
zawiera element , a ponieważN3
importuje oba, odwoływanie się w elemencieA
N3
jest błędem czasu kompilacji. W takiej sytuacji konflikt można rozwiązać za pomocą kwalifikacji odwołań doA
, lub wprowadzając using_alias_directive , który wybiera określonyA
element . 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łonkowskiA
typu iC
zawiera poleA
statyczne , 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 typyN1.A
są dostępne bezpośrednio, a zatem metodaN
może odwoływać się zarównoB
do elementówN1.A
członkowskich , jak iM
.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ą wN1.A
metodzie , ale tylko jako metodę rozszerzenia. W związku z tym pierwsze odwołanie doM
elementu w treściB.N
wyników powoduje błąd czasu kompilacji, ponieważ żadne elementy członkowskie o nazwieM
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ą wN1.B
pliku , ale nie importuje metodyM
zawartej wN1.A
pliku . W związku z tym odwołanie doM
elementu w treściC.N
wyników jest błędem czasu kompilacji, ponieważ żadne elementy członkowskie o nazwieM
nie są w zakresie. Deweloperzy muszą dodać drugąusing static
dyrektywę, aby określić, że metody wN1.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.N
deklaracji.
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 lubinternal
. Wartość domyślna tointernal
dostęp. - Typy zadeklarowane w klasach mogą mieć
public
dostęp , ,protected internal
protected
,private protected
,internal
lubprivate
. Wartość domyślna toprivate
dostęp. - Typy zadeklarowane w strukturach mogą mieć
public
dostęp ,internal
lubprivate
. Wartość domyślna toprivate
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ₑ>
, gdzieN
iI
reprezentują identyfikatory, i<A₁, ..., Aₑ>
jest listą argumentów typu. (e
jest zawsze co najmniej jeden).N::I
, gdzieN
iI
reprezentują identyfikatory. (W tym przypadkue
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 identyfikatoremglobal
, wyszukiwanaI
jest globalna przestrzeń nazw:- Jeśli globalna przestrzeń nazw zawiera przestrzeń nazw o nazwie
I
ie
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
ie
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 mae
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.
- Jeśli globalna przestrzeń nazw zawiera przestrzeń nazw o nazwie
- 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 nazwieI
ie
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 nazwieI
ie
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 nazwieI
, który mae
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.
- Jeśli przestrzeń nazw skojarzona z
- 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ętaglobal::A
, a typSystem.Net.Sockets.Socket
jest przywołyyny za pomocąS::Socket
polecenia . Użycie elementuA.x
iS.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 nazwieglobal
w zakresie. Jeśli niektóre jednostki o nazwie global były w zakresie, wówczasglobal
global.A
w poleceniu zostałaby rozpoznana dla tej jednostki.Użycie
global
jako identyfikatora po lewej stronie qualified_alias_member zawsze powoduje wyszukiwanie wglobal
przestrzeni nazw, nawet jeśli istnieje alias o nazwieglobal
. 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.A
MyGlobalTypes.A
rozpoznaje iglobal::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 klasaA
, jak i aliasA
using są w zakresie. Z tego powodu użycie elementuA
w kwalifikowanej nazwieA.Stream
jest niejednoznaczne i powoduje wystąpienie błędu czasu kompilacji. Jednak użycie elementuA
z::
kwalifikatorem nie jest błędem, ponieważA
jest ona sprawdzana tylko jako alias przestrzeni nazw.przykład końcowy
ECMA C# draft specification