14 Namespaces
14.1 Allgemein
C#-Programme werden mithilfe von Namespaces organisiert. Namespaces werden sowohl als "internes" Organisationssystem für ein Programm als auch als ein "externes" Organisationssystem verwendet – eine Möglichkeit, Programmelemente darzustellen, die anderen Programmen offengelegt werden.
Die Verwendung von Direktiven (§14.5) wird bereitgestellt, um die Verwendung von Namespaces zu erleichtern.
14.2 Kompilierungseinheiten
Ein compilation_unit besteht aus null oder mehr extern_alias_directivegefolgt von Null oder mehr using_directivegefolgt von Null oder einer global_attributes gefolgt von Null oder mehr namespace_member_declarations. Die compilation_unit definiert die gesamtstruktur der Eingabe.
compilation_unit
: extern_alias_directive* using_directive* global_attributes?
namespace_member_declaration*
;
Ein C#-Programm besteht aus einer oder mehreren Kompilierungseinheiten. Wenn ein C#-Programm kompiliert wird, werden alle Kompilierungseinheiten zusammen verarbeitet. So können Kompilierungseinheiten voneinander abhängig sein, möglicherweise in kreisförmiger Weise.
Die extern_alias_directiveeiner Kompilierungseinheit wirken sich auf die using_directives, global_attributes und namespace_member_declarationdieser Kompilierungseinheit aus, wirken sich jedoch nicht auf andere Kompilierungseinheiten aus.
Die using_directiveeiner Kompilierungseinheit wirken sich auf die global_attributes und namespace_member_declarationdieser Kompilierungseinheit aus, wirken sich jedoch nicht auf andere Kompilierungseinheiten aus.
Die global_attributes (§22.3) einer Kompilierungseinheit ermöglichen die Spezifikation von Attributen für die Zielassembly und das Zielmodul. Assemblys und Module dienen als physische Container für Typen. Eine Assembly kann aus mehreren physisch getrennten Modulen bestehen.
Die namespace_member_declarationjeder Kompilierungseinheit eines Programms tragen zu einem einzigen Deklarationsraum, der als globaler Namespace bezeichnet wird, bei.
Beispiel:
// File A.cs: class A {} // File B.cs: class B {}
Die beiden Kompilierungseinheiten tragen zum einzelnen globalen Namespace bei, in diesem Fall zwei Klassen mit den vollqualifizierten Namen
A
undB
. Da die beiden Kompilierungseinheiten zum gleichen Deklarationsraum beitragen, wäre es ein Fehler gewesen, wenn jede eine Deklaration eines Elements mit demselben Namen enthielt.Endbeispiel
14.3 Namespacedeklarationen
Ein namespace_declaration besteht aus dem Schlüsselwortnamespace, gefolgt von einem Namespacenamen und -text, optional gefolgt von einem Semikolon.
namespace_declaration
: 'namespace' qualified_identifier namespace_body ';'?
;
qualified_identifier
: identifier ('.' identifier)*
;
namespace_body
: '{' extern_alias_directive* using_directive*
namespace_member_declaration* '}'
;
Eine namespace_declaration kann als Deklaration der obersten Ebene in einer compilation_unit oder als Memberdeklaration innerhalb einer anderen namespace_declaration auftreten. Wenn ein namespace_declaration als Deklaration der obersten Ebene in einem compilation_unit auftritt, wird der Namespace mitglied des globalen Namespace. Wenn ein namespace_declaration innerhalb einer anderen namespace_declaration auftritt, wird der innere Namespace zu einem Mitglied des äußeren Namespaces. In beiden Fällen muss der Name eines Namespace innerhalb des enthaltenden Namespace eindeutig sein.
Namespaces sind implizit public
und die Deklaration eines Namespaces darf keine Zugriffsmodifizierer enthalten.
Innerhalb eines namespace_body importieren die optionalen using_directivedie Namen anderer Namespaces, Typen und Member, sodass sie direkt anstelle von qualifizierten Namen referenziert werden können. Die optionalen namespace_member_declarationtragen Elemente zum Deklarationsraum des Namespaces bei. Beachten Sie, dass alle using_directivevor den Mitgliedserklärungen erscheinen.
Die qualified_identifier eines namespace_declaration kann ein einzelner Bezeichner oder eine Sequenz von Bezeichnern sein, die durch ".
" Token getrennt sind. In letzterer Form kann ein Programm einen geschachtelten Namespace definieren, ohne mehrere Namespacedeklarationen lexikalisch zu verschachteln.
Beispiel:
namespace N1.N2 { class A {} class B {} }
ist semantisch gleichbedeutend mit
namespace N1 { namespace N2 { class A {} class B {} } }
Endbeispiel
Namespaces sind offen beendet, und zwei Namespacedeklarationen mit demselben vollqualifizierten Namen (§7.8.2) tragen zum gleichen Deklarationsraum (§7.3) bei.
Beispiel: Im folgenden Code
namespace N1.N2 { class A {} } namespace N1.N2 { class B {} }
Die beiden oben genannten Namespacedeklarationen tragen zum gleichen Deklarationsraum bei, in diesem Fall zwei Klassen mit den vollqualifizierten Namen
N1.N2.A
undN1.N2.B
. Da die beiden Deklarationen zum gleichen Deklarationsraum beitragen, wäre es ein Fehler gewesen, wenn jede eine Deklaration eines Elements mit demselben Namen enthielt.Endbeispiel
14.4 Externe Aliasdirektiven
Ein extern_alias_directive führt einen Bezeichner ein, der als Alias für einen Namespace dient. Die Spezifikation des aliasierten Namespaces ist außerhalb des Quellcodes des Programms und gilt auch für geschachtelte Namespaces des aliasierten Namespaces.
extern_alias_directive
: 'extern' 'alias' identifier ';'
;
Der Umfang eines extern_alias_directive erstreckt sich über die using_directive, global_attributes und namespace_member_declarationseiner unmittelbar enthaltenden compilation_unit oder namespace_body.
Innerhalb einer Kompilierungseinheit oder eines Namespacetexts, der eine extern_alias_directive enthält, kann der vom extern_alias_directive eingeführte Bezeichner verwendet werden, um auf den aliasierten Namespace zu verweisen. Es handelt sich um einen Kompilierzeitfehler für den Bezeichner, der das Wort global
sein soll.
Der von einem extern_alias_directive eingeführte Alias ähnelt dem alias, der von einem using_alias_directive eingeführt wurde. Ausführlichere Erläuterungen zu extern_alias_directive und using_alias_directivefinden Sie unter §14.5.2.
alias
ist ein kontextbezogenes Schlüsselwort (§6.4.4) und hat nur eine besondere Bedeutung, wenn es unmittelbar auf das extern
Schlüsselwort in einem extern_alias_directive folgt.
Wenn ein Programm einen externen Alias deklariert, für den keine externe Definition bereitgestellt wird, tritt ein Fehler auf.
Beispiel: Das folgende Programm deklariert und verwendet zwei externe Aliase und
X
Y
, die jeweils den Stamm einer unterschiedlichen Namespacehierarchie darstellen:extern alias X; extern alias Y; class Test { X::N.A a; X::N.B b1; Y::N.B b2; Y::N.C c; }
Das Programm deklariert das Vorhandensein der externen Aliase
X
undY
, aber die tatsächlichen Definitionen der Aliase sind außerhalb des Programms. Auf die identisch benanntenN.B
Klassen kann nun unterX.N.B
Verwendung des NamespacealiasqualifizierersX::N.B
undY.N.B
desY::N.B
Namespaces verwiesen werden. Endbeispiel
14.5 Verwenden von Direktiven
14.5.1 Allgemein
Die Verwendung von Direktiven erleichtert die Verwendung von Namespaces und Typen, die in anderen Namespaces definiert sind. Die Verwendung von Direktiven wirkt sich auf den Namensauflösungsprozess von namespace_or_type_name s (§7.8) und simple_names (§12.8.4) aus, aber im Gegensatz zu Deklarationen tragen using_directives nicht zu den zugrunde liegenden Deklarationsräumen der Kompilierungseinheiten oder Namespaces bei, in denensie verwendet werden.
using_directive
: using_alias_directive
| using_namespace_directive
| using_static_directive
;
Ein using_alias_directive (§14.5.2) führt einen Alias für einen Namespace oder Typ ein.
Ein using_namespace_directive (§14.5.3) importiert die Typmember eines Namespaces.
Ein using_static_directive (§14.5.4) importiert die geschachtelten Typen und statischen Member eines Typs.
Der Umfang eines using_directive erstreckt sich über die namespace_member_declarations seines unmittelbar enthaltenden Kompilierungseinheits- oder Namespacetexts. Der Umfang eines using_directive schließt insbesondere nicht den Peer using_directives ein. Daher wirken sich Peer using_directivenicht gegenseitig aus, und die Reihenfolge, in der sie geschrieben werden, ist unbedeutend. Im Gegensatz dazu enthält der Bereich eines extern_alias_directive die in derselben Kompilierungseinheit oder im Namespacetext definierten using_directive.
14.5.2 Verwenden von Aliasdirektiven
Ein using_alias_directive führt einen Bezeichner ein, der als Alias für einen Namespace oder Typ innerhalb der unmittelbar eingeschlossenen Kompilierungseinheit oder des Namespacetexts dient.
using_alias_directive
: 'using' identifier '=' namespace_or_type_name ';'
;
Innerhalb globaler Attribute und Memberdeklarationen in einer Kompilierungseinheit oder einem Namespacetext, die ein using_alias_directive enthält, kann der vom using_alias_directive eingeführte Bezeichner verwendet werden, um auf den angegebenen Namespace oder Typ zu verweisen.
Beispiel:
namespace N1.N2 { class A {} } namespace N3 { using A = N1.N2.A; class B: A {} }
Oben, innerhalb von Memberdeklarationen im
N3
Namespace,A
ist ein Alias fürN1.N2.A
, und somit wird die KlasseN3.B
von der KlasseN1.N2.A
abgeleitet. Derselbe Effekt kann durch Erstellen eines AliasR
fürN1.N2
und anschließendes VerweisenR.A
abgerufen werden:namespace N3 { using R = N1.N2; class B : R.A {} }
Endbeispiel
Innerhalb der Verwendung von Direktiven, globalen Attributen und Memberdeklarationen in einer Kompilierungseinheit oder einem Namespacetext, die ein extern_alias_directive enthält, kann der vom extern_alias_directive eingeführte Bezeichner verwendet werden, um auf den zugeordneten Namespace zu verweisen.
Beispiel:
namespace N1 { extern alias N2; class B : N2::A {} }
Oben, innerhalb von Memberdeklarationen im
N1
Namespace,N2
ist ein Alias für einige Namespaces, deren Definition außerhalb des Quellcodes des Programms ist. Die KlasseN1.B
wird von der KlasseN2.A
abgeleitet. Derselbe Effekt kann durch Erstellen eines AliasA
fürN2.A
und anschließendes VerweisenA
abgerufen werden:namespace N1 { extern alias N2; using A = N2::A; class B : A {} }
Endbeispiel
Ein extern_alias_directive oder using_alias_directive stellt einen Alias in einer bestimmten Kompilierungseinheit oder einem Namespacetext zur Verfügung, trägt jedoch keine neuen Member zum zugrunde liegenden Deklarationsbereich bei. Anders ausgedrückt: Eine Aliasdirektive ist nicht transitiv, sondern wirkt sich vielmehr nur auf die Kompilierungseinheit oder den Namespacetext aus, in dem sie auftritt.
Beispiel: Im folgenden Code
namespace N3 { extern alias R1; using R2 = N1.N2; } namespace N3 { class B : R1::A, R2.I {} // Error, R1 and R2 unknown }
die Bereiche der Aliasdirektiven, die Memberdeklarationen im Namespacetext einführen
R1
undR2
nur erweitern, in dem sie enthalten sind, undR2
sind daherR1
in der zweiten Namespacedeklaration unbekannt. Wenn Sie jedoch die Aliasdirektiven in die enthaltende Kompilierungseinheit setzen, wird der Alias in beiden Namespacedeklarationen verfügbar:extern alias R1; using R2 = N1.N2; namespace N3 { class B : R1::A, R2.I {} } namespace N3 { class C : R1::A, R2.I {} }
Endbeispiel
Jede extern_alias_directive oder using_alias_directive in einem compilation_unit oder namespace_body trägt zum Aliasdeklarationsraum (§7.3) des unmittelbar eingeschlossenen compilation_unit oder namespace_body bei. Der Bezeichner der Aliasdirektive muss innerhalb des entsprechenden Aliasdeklarationsbereichs eindeutig sein. Der Aliasbezeichner muss nicht innerhalb des globalen Deklarationsbereichs oder des Deklarationsraums des entsprechenden Namespace eindeutig sein.
Beispiel:
extern alias X; extern alias Y; using X = N1.N2; // Error: alias X already exists class Y {} // Ok
Der benannte
X
Alias verursacht einen Fehler, da bereits ein Alias vorhanden ist, der in derselben Kompilierungseinheit benanntX
ist. Die benannteY
Klasse steht nicht im Konflikt mit dem externen Alias, der benanntY
wird, da diese Namen zu unterschiedlichen Deklarationsräumen hinzugefügt werden. Der erste wird dem globalen Deklarationsraum hinzugefügt, und letzteres wird dem Aliasdeklarationsraum für diese Kompilierungseinheit hinzugefügt.Wenn ein Aliasname mit dem Namen eines Mitglieds eines Namespace übereinstimmt, muss die Verwendung einer der Beiden entsprechend qualifiziert sein:
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 }
Im zweiten Namespacetext für
N3
, nicht qualifizierte Verwendung vonB
Ergebnissen in einem Fehler, daN3
enthält ein Element benanntB
und der Namespacetext, der auch einen Alias mit NamenB
deklariert ; ebenso fürA
. Auf die KlasseN3.B
kann verwiesen werden alsN3.B
oderglobal::N3.B
. Der AliasA
kann in einem qualified-alias-member (§14.8) verwendet werden, zA::B
. B. . Der AliasB
ist im Wesentlichen nutzlos. Sie kann nicht in einer qualified_alias_member verwendet werden, da nur Namespacealias in einem qualified_alias_member undB
Aliasen eines Typs verwendet werden können.Endbeispiel
Genau wie normale Member werden namen, die von alias_directives eingeführt werden, von ähnlich benannten Mitgliedern in geschachtelten Bereichen ausgeblendet.
Beispiel: Im folgenden Code
using R = N1.N2; namespace N3 { class R {} class B: R.A {} // Error, R has no member A }
der Verweis in
R.A
der Deklaration derB
Ursache für einen Kompilierungszeitfehler, daR
sich auf , nichtN1.N2
aufN3.R
.Endbeispiel
Die Reihenfolge, in der extern_alias_directivegeschrieben werden, hat keine Bedeutung. Ebenso hat die Reihenfolge, in der using_alias_directivegeschrieben werden, keine Bedeutung, aber alle using_alias_directives müssen nach allen extern_alias_directives in derselben Kompilierungseinheit oder im selben Namespacetext kommen. Die Auflösung der namespace_or_type_name, auf die von einem using_alias_directive verwiesen wird, ist nicht von der using_alias_directive selbst oder von anderen using_directiveim unmittelbar enthaltenden Kompilierungseinheit oder Namespacetext betroffen, kann aber von extern_alias_directiveim unmittelbar enthaltenden Kompilierungseinheit oder Namespacetext betroffen sein. Anders ausgedrückt: Die namespace_or_type_name eines using_alias_directive wird aufgelöst, als ob die sofort enthaltende Kompilierungseinheit oder der Namespacetext keine using_directivehatte, sondern über den richtigen Satz von extern_alias_directives verfügt.
Beispiel: Im folgenden Code
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 }
die letzte using_alias_directive führt zu einem Kompilierungszeitfehler, da sie von der vorherigen using_alias_directive nicht betroffen ist. Der erste using_alias_directive führt nicht zu einem Fehler, da der Bereich des externen Alias X die using_alias_directive enthält.
Endbeispiel
Ein using_alias_directive kann einen Alias für einen beliebigen Namespace oder Typ erstellen, einschließlich des Namespaces, in dem er angezeigt wird, sowie jeden Namespace oder Typ, der in diesem Namespace geschachtelt ist.
Der Zugriff auf einen Namespace oder Typ über einen Alias führt genau zum gleichen Ergebnis wie der Zugriff auf diesen Namespace oder Typ über seinen deklarierten Namen.
Beispiel: Gegeben
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 } }
die Namen
N1.N2.A
,R1.N2.A
undR2.A
sind gleichwertig und alle verweisen auf die Klassendeklaration, deren vollqualifizierter Name istN1.N2.A
.Endbeispiel
Obwohl jeder Teil eines Teiltyps (§15.2.7) innerhalb desselben Namespace deklariert wird, werden die Teile in der Regel in verschiedenen Namespacedeklarationen geschrieben. Somit können für jeden Teil unterschiedliche extern_alias_directiveund using_directives vorhanden sein. Bei der Interpretation einfacher Namen (§12.8.4) innerhalb eines Teils werden nur die extern_alias_directives und using_directiveder Namespacetexte und kompilierungseinheit berücksichtigt, die diesen Teil einschließt. Dies kann dazu führen, dass derselbe Bezeichner unterschiedliche Bedeutungen in verschiedenen Teilen hat.
Beispiel:
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 } }
Endbeispiel
Die Verwendung von Aliasen kann einen geschlossenen konstruierten Typ benennen, aber keine ungebundene generische Typdeklaration ohne Angabe von Typargumenten benennen.
Beispiel:
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 }
Endbeispiel
14.5.3 Verwenden von Namespacedirektiven
Ein using_namespace_directive importiert die in einem Namespace enthaltenen Typen in den unmittelbar eingeschlossenen Kompilierungseinheits- oder Namespacetext, wodurch der Bezeichner jedes Typs ohne Qualifikation verwendet werden kann.
using_namespace_directive
: 'using' namespace_name ';'
;
Innerhalb von Memberdeklarationen in einer Kompilierungseinheit oder einem Namespacetext, die ein using_namespace_directive enthält, können die im angegebenen Namespace enthaltenen Typen direkt referenziert werden.
Beispiel:
namespace N1.N2 { class A {} } namespace N3 { using N1.N2; class B : A {} }
Oben sind innerhalb von Memberdeklarationen im
N3
Namespace die TypmemberN1.N2
direkt verfügbar und somitN3.B
von der KlasseN1.N2.A
abgeleitet.Endbeispiel
Ein using_namespace_directive importiert die typen, die im angegebenen Namespace enthalten sind, jedoch keine geschachtelten Namespaces.
Beispiel: Im folgenden Code
namespace N1.N2 { class A {} } namespace N3 { using N1; class B : N2.A {} // Error, N2 unknown }
die using_namespace_directive importiert die in
N1
, aber nicht die inN1
geschachtelten Namespaces . Der Verweis inN2.A
der Deklaration vonB
Ergebnissen führt daher zu einem Kompilierungszeitfehler, da keine benanntenN2
Elemente im Bereich liegen.Endbeispiel
Im Gegensatz zu einem using_alias_directive kann ein using_namespace_directive Typen importieren, deren Bezeichner bereits in der eingeschlossenen Kompilierungseinheit oder im Namespacetext definiert sind. Tatsächlich werden von einem using_namespace_directive importierte Namen von ähnlich benannten Elementen in der eingeschlossenen Kompilierungseinheit oder im Namespacetext ausgeblendet.
Beispiel:
namespace N1.N2 { class A {} class B {} } namespace N3 { using N1.N2; class A {} }
Hier bezieht sich
N3.A
innerhalb von Memberdeklarationen imN3
NamespaceA
anstelle vonN1.N2.A
.Endbeispiel
Da Namen möglicherweise mehrdeutig sind, wenn mehr als ein importierter Namespace denselben Typnamen einführt, ist eine using_alias_directive nützlich, um den Verweis zu disambiguieren.
Beispiel: Im folgenden Code
namespace N1 { class A {} } namespace N2 { class A {} } namespace N3 { using N1; using N2; class B : A {} // Error, A is ambiguous }
sowohl als auch
N1
ein ElementA
enthaltenN2
, und daN3
beide importiert werden, ist das VerweisenA
inN3
einen Kompilierungszeitfehler. In dieser Situation kann der Konflikt entweder durch die Qualifizierung von VerweisenA
auf oder durch Einführung einer using_alias_directive gelöst werden, die eine bestimmteA
Auswahl vornimmt. Zum Beispiel:namespace N3 { using N1; using N2; using A = N1.A; class B : A {} // A means N1.A }
Endbeispiel
Wenn darüber hinaus mehrere Namespaces oder Typen, die von using_namespace_directives oder using_static_directives in derselben Kompilierungseinheit oder im selben Namespacetext importiert werden, Typen oder Member mit demselben Namen enthalten, werden Verweise auf diesen Namen als simple_name als mehrdeutig angesehen.
Beispiel:
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
enthält ein TypelementA
undC
enthält ein statisches FeldA
, und daN2
beide importiert werden, ist das VerweisenA
auf eine simple_name mehrdeutig und ein Kompilierungszeitfehler.Endbeispiel
Wie ein using_alias_directive trägt ein using_namespace_directive keine neuen Member zum zugrunde liegenden Deklarationsraum der Kompilierungseinheit oder des Namespaces bei, sondern betrifft vielmehr nur die Kompilierungseinheit oder den Namespacetext, in der er angezeigt wird.
Die namespace_name, auf die von einem using_namespace_directive verwiesen wird, wird auf die gleiche Weise aufgelöst wie die namespace_or_type_name, auf die von einem using_alias_directive verwiesen wird. Daher wirken sich using_namespace_directives in derselben Kompilierungseinheit oder im Namespacetext nicht gegenseitig aus und können in beliebiger Reihenfolge geschrieben werden.
14.5.4 Verwenden statischer Direktiven
Ein using_static_directive importiert die geschachtelten Typen und statischen Elemente, die direkt in einer Typdeklaration enthalten sind, in den unmittelbar eingeschlossenen Kompilierungseinheit oder Namespacetext, wodurch der Bezeichner jedes Elements und Typs ohne Qualifikation verwendet werden kann.
using_static_directive
: 'using' 'static' type_name ';'
;
Innerhalb von Memberdeklarationen in einer Kompilierungseinheit oder einem Namespacetext, die eine using_static_directive enthält, können direkt auf die in der Deklaration des angegebenen Typs enthaltenen verschachtelten Typen und statische Member (mit Ausnahme von Erweiterungsmethoden) direkt verwiesen werden.
Beispiel:
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(); } } }
Im vorherigen Code sind innerhalb von Memberdeklarationen im
N2
Namespace die statischen Member und geschachtelten TypenN1.A
direkt verfügbar und somit kann die MethodeN
sowohl auf die AlsM
auch auf dieB
Member vonN1.A
verweisen.Endbeispiel
Eine using_static_directive importiert Erweiterungsmethoden nicht direkt als statische Methoden, stellt sie aber für Den Aufruf der Erweiterungsmethode (§12.8.9.3) zur Verfügung.
Beispiel:
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 } } }
die using_static_directive importiert die in
N1.A
, aber nur als Erweiterungsmethode enthaltene ErweiterungsmethodeM
. Der erste Verweis imM
Textkörper führtB.N
daher zu einem Kompilierungszeitfehler, da keine benanntenM
Elemente im Bereich liegen.Endbeispiel
Ein using_static_directive importiert nur Elemente und Typen, die direkt im angegebenen Typ deklariert sind, nicht Elemente und Typen, die in Basisklassen deklariert sind.
Beispiel:
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 } } }
die using_static_directive importiert die
M2
methodeN1.B
enthalten, importiert jedoch nicht dieM
inN1.A
. Der Verweis imM
Textkörper führtC.N
daher zu einem Kompilierungszeitfehler, da keine benanntenM
Elemente im Bereich liegen. Entwickler müssen eine zweiteusing static
Direktive hinzufügen, um anzugeben, dass die MethodenN1.A
ebenfalls importiert werden sollen.Endbeispiel
Mehrdeutigkeiten zwischen mehreren using_namespace_directives und using_static_directives werden in §14.5.3 erläutert.
14.6 Namespace-Memberdeklarationen
Eine namespace_member_declaration ist entweder ein namespace_declaration (§14.3) oder ein type_declaration (§14.7).
namespace_member_declaration
: namespace_declaration
| type_declaration
;
Eine Kompilierungseinheit oder ein Namespacetext kann namespace_member_declarations enthalten, und solche Deklarationen tragen zum zugrunde liegenden Deklarationsraum der enthaltenden Kompilierungseinheit oder des Namespacetexts bei.
14.7 Typdeklarationen
Ein type_declaration ist ein class_declaration (§15.2), ein struct_declaration (§16.2), ein interface_declaration (§18.2), ein enum_declaration (§19.2) oder ein delegate_declaration (§20.2).
type_declaration
: class_declaration
| struct_declaration
| interface_declaration
| enum_declaration
| delegate_declaration
;
Eine type_declaration kann als Deklaration der obersten Ebene in einer Kompilierungseinheit oder als Memberdeklaration innerhalb eines Namespaces, einer Klasse oder einer Struktur auftreten.
Wenn eine Typdeklaration für einen Typ T
als Deklaration der obersten Ebene in einer Kompilierungseinheit auftritt, entspricht der vollqualifizierte Name (§7.8.2) der Typdeklaration dem nicht qualifizierten Namen der Deklaration (§7.8.2). Wenn eine Typdeklaration für einen Typ T
in einem Namespace, einer Klasse oder einer Strukturdeklaration auftritt, ist der vollqualifizierte Name (§7.8.3) der Typdeklarationen S.N
, wobei S
der vollqualifizierte Name der enthaltenden Namespace-, Klassen- oder Strukturdeklaration ist und N
der nicht qualifizierte Name der Deklaration ist.
Ein in einer Klasse oder Struktur deklarierter Typ wird als geschachtelter Typ (§15.3.9) bezeichnet.
Die Zulässigen Zugriffsmodifizierer und der Standardzugriff für eine Typdeklaration hängen vom Kontext ab, in dem die Deklaration stattfindet (§7.5.2):
- Typen, die in Kompilierungseinheiten oder Namespaces deklariert sind, können oder
public
internal
darauf zugreifen. Der Standard istinternal
Der Zugriff. - In Klassen deklarierte Typen können über , ,
protected internal
, , oderprivate
internal
Zugriff verfügenpublic
.private protected
protected
Der Standard istprivate
Der Zugriff. - In Struktur deklarierte Typen können über ,
internal
oderprivate
Zugriff verfügenpublic
. Der Standard istprivate
Der Zugriff.
14.8 Qualifiziertes Alias-Mitglied
14.8.1 Allgemein
Der Namespacealiasqualifizierer ::
ermöglicht es, sicherzustellen, dass Typnamensuchvorgänge durch die Einführung neuer Typen und Member nicht betroffen sind. Der Namespacealiasqualifizierer wird immer zwischen zwei Bezeichnern angezeigt, die als Links- und Rechts-IDs bezeichnet werden. Im Gegensatz zum regulären .
Qualifizierer wird der linke Bezeichner des ::
Qualifizierers nur als extern oder mit Alias nachschlagen.
Ein qualified_alias_member bietet expliziten Zugriff auf den globalen Namespace und externe oder verwendung von Aliasen, die potenziell von anderen Entitäten ausgeblendet werden.
qualified_alias_member
: identifier '::' identifier type_argument_list?
;
Ein qualified_alias_member kann als namespace_or_type_name (§7.8) oder als linker Operand in einem member_access (§12.8.7) verwendet werden.
Ein qualified_alias_member besteht aus zwei Bezeichnern, die als Links- und Rechtshand-IDs bezeichnet werden, getrennt vom ::
Token und optional gefolgt von einem type_argument_list. Wenn der linkshändige Bezeichner global ist, wird der globale Namespace nach dem rechten Bezeichner gesucht. Bei anderen linkshändigen Bezeichnern wird dieser Bezeichner als extern oder unter Verwendung von Aliasen (§14.4 und §14.5.2) nachschlagen. Wenn kein solcher Alias vorhanden ist oder der Alias auf einen Typ verweist, tritt ein Kompilierungszeitfehler auf. Wenn der Alias auf einen Namespace verweist, wird dieser Namespace nach dem rechten Bezeichner gesucht.
Eine qualified_alias_member hat eine von zwei Formen:
N::I<A₁, ..., Aₑ>
, wobeiN
undI
stellen Sie Bezeichner dar und<A₁, ..., Aₑ>
ist eine Typargumentliste. (e
ist immer mindestens eins.)N::I
, wobeiN
undI
stellen Sie Bezeichner dar. (In diesem Falle
wird als Null betrachtet.)
Bei Verwendung dieser Schreibweise wird die Bedeutung einer qualified_alias_member wie folgt bestimmt:
- Wenn
N
es sich um den Bezeichnerglobal
handelt, wird der globale Namespace durchsuchtI
:- Wenn der globale Namespace einen Namespace mit dem Namen
I
Null enthält,e
bezieht sich die qualified_alias_member auf diesen Namespace. - Andernfalls bezieht sich der qualified_alias_member auf diesen Typ, wenn der globale Namespace einen nicht generischen Typ mit dem Namen
I
e
Null enthält. - Andernfalls bezieht sich die qualified_alias_member auf diesen Typ, der mit den angegebenen Typargumenten erstellt wurde, wenn der globale Namespace einen Typ
I
e
mit Typparametern enthält. - Andernfalls ist die qualified_alias_member nicht definiert, und ein Kompilierungszeitfehler tritt auf.
- Wenn der globale Namespace einen Namespace mit dem Namen
- Andernfalls werden ab der Namespacedeklaration (§14.3) sofort die qualified_alias_member (falls vorhanden) mit jeder eingeschlossenen Namespacedeklaration (falls vorhanden) fortgesetzt und mit der Kompilierungseinheit mit dem qualified_alias_member enden, die folgenden Schritte ausgewertet, bis sich eine Entität befindet:
- Wenn die Namespacedeklaration oder Kompilierungseinheit eine using_alias_directive enthält, die N einem Typ zuordnet, ist die qualified_alias_member nicht definiert, und ein Kompilierungszeitfehler tritt auf.
- Andernfalls, wenn die Namespacedeklaration oder Kompilierungseinheit eine extern_alias_directive oder using_alias_directive enthält, die einem Namespace zugeordnet ist
N
, dann:- Wenn der mit dem Namespace verknüpfte
N
Namespace einen Namespace mit dem NamenI
Null enthält,e
bezieht sich die qualified_alias_member auf diesen Namespace. - Andernfalls bezieht sich die qualified_alias_member auf diesen Typ, wenn der zugeordnete
N
Namespace einen nicht generischen Typ mit dem NamenI
e
Null enthält. - Andernfalls bezieht sich die qualified_alias_member auf diesen Typ, der mit den angegebenen Typargumenten erstellt wurde, wenn der mit einem Typ verknüpfte
N
Namespace einen TypI
mit Typparametern enthälte
. - Andernfalls ist die qualified_alias_member nicht definiert, und ein Kompilierungszeitfehler tritt auf.
- Wenn der mit dem Namespace verknüpfte
- Andernfalls ist die qualified_alias_member nicht definiert, und ein Kompilierungszeitfehler tritt auf.
Beispiel: Im Code:
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; } }
auf die Klasse
A
wird verwiesenglobal::A
, und auf den TypSystem.Net.Sockets.Socket
wird mitS::Socket
. Die VerwendungA.x
undS.Socket
stattdessen haben Kompilierungszeitfehler verursacht, daA
sieS
in die Parameter aufgelöst wurden.Endbeispiel
Hinweis: Der Bezeichner hat nur eine besondere Bedeutung, wenn er als linker Bezeichner
global
eines qualified_alias_name verwendet wird. Es handelt sich nicht um ein Schlüsselwort und es handelt sich nicht selbst um einen Alias; es handelt sich um ein kontextbezogenes Schlüsselwort (§6.4.4). Im Code:class A { } class C { global.A x; // Error: global is not defined global::A y; // Valid: References A in the global namespace }
using
global.A
verursacht einen Kompilierungszeitfehler, da keine Entität im Bereich benanntglobal
ist. Wenn eine Entität mit dem Namen "Global" den Bereich hat, wäre diesglobal
global.A
in diese Entität aufgelöst worden.Die Verwendung
global
als linker Bezeichner eines qualified_alias_member führt immer zu einer Suche imglobal
Namespace, auch wenn ein verwendeter Alias mit dem Namen vorhandenglobal
ist. Im Code: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
wird inMyGlobalTypes.A
die KlasseA
im globalen Namespace aufgelöst undglobal::A
aufgelöst.Endnote
14.8.2 Eindeutigkeit von Aliasen
Jede Kompilierungseinheit und jeder Namespacetext verfügt über einen separaten Deklarationsraum für externe Aliase und die Verwendung von Aliasen. Während der Name eines externen Alias oder eines Alias innerhalb der Gruppe externer Aliase eindeutig sein muss und Aliase verwendet werden, die in der unmittelbar enthaltenen Kompilierungseinheit oder im Namespacetext deklariert sind, darf ein Alias denselben Namen wie ein Typ oder Namespace aufweisen, solange er nur mit dem ::
Qualifizierer verwendet wird.
Beispiel: Im Folgenden:
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 } }
der Name
A
hat zwei mögliche Bedeutungen im zweiten Namespacetext, da sowohl die KlasseA
als auch der using-AliasA
im Bereich liegen. Aus diesem Grund ist die Verwendung desA
qualifizierten NamensA.Stream
mehrdeutig und verursacht einen Kompilierungszeitfehler. Die Verwendung mitA
dem::
Qualifizierer ist jedoch kein Fehler, daA
nur als Namespacealias nachschlagen wird.Endbeispiel
ECMA C# draft specification