Partager via


14 espaces de noms

14.1 Général

Les programmes C# sont organisés à l’aide d’espaces de noms. Les espaces de noms sont utilisés à la fois comme système d’organisation « interne » pour un programme, et comme système d’organisation « externe », un moyen de présenter des éléments de programme exposés à d’autres programmes.

L’utilisation de directives (§14.5) est fournie pour faciliter l’utilisation des espaces de noms.

14.2 Unités de compilation

Un compilation_unit se compose de zéro ou plusieurs extern_alias_directivesuivis de zéro ou plus de using_directives suivis de zéro ou d’un global_attributes suivi de zéro ou plus de namespace_member_declarations. La compilation_unit définit la structure globale de l’entrée.

compilation_unit
    : extern_alias_directive* using_directive* global_attributes?
      namespace_member_declaration*
    ;

Un programme C# se compose d’une ou plusieurs unités de compilation. Lorsqu’un programme C# est compilé, toutes les unités de compilation sont traitées ensemble. Ainsi, les unités de compilation peuvent dépendre les unes des autres, éventuellement de manière circulaire.

Les extern_alias_directived’une unité de compilation affectent les using_directive, les global_attributes et les namespace_member_declarationde cette unité de compilation, mais n’ont aucun effet sur d’autres unités de compilation.

Les using_directived’une unité de compilation affectent les global_attributes et les namespace_member_declarationde cette unité de compilation, mais n’ont aucun effet sur d’autres unités de compilation.

La global_attributes (§22.3) d’une unité de compilation permet la spécification des attributs pour l’assembly et le module cibles. Les assemblys et les modules agissent en tant que conteneurs physiques pour les types. Un assembly peut se composer de plusieurs modules physiquement distincts.

Les namespace_member_declarationde chaque unité de compilation d’un programme contribuent aux membres à un espace de déclaration unique appelé espace de noms global.

Exemple :

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

Les deux unités de compilation contribuent à l’espace de noms global unique, dans ce cas, déclarant deux classes avec les noms complets A et B. Étant donné que les deux unités de compilation contribuent au même espace de déclaration, il aurait été une erreur si chacune contenait une déclaration d’un membre portant le même nom.

exemple de fin

14.3 Déclarations d’espace de noms

Un namespace_declaration se compose de l’espace de noms de mot clé, suivi d’un nom et d’un corps d’espace de noms, éventuellement suivis d’un point-virgule.

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

qualified_identifier
    : identifier ('.' identifier)*
    ;

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

Une namespace_declaration peut se produire sous la forme d’une déclaration de niveau supérieur dans un compilation_unit ou en tant que déclaration de membre au sein d’une autre namespace_declaration. Lorsqu’un namespace_declaration se produit en tant que déclaration de niveau supérieur dans un compilation_unit, l’espace de noms devient membre de l’espace de noms global. Lorsqu’un namespace_declaration se produit dans une autre namespace_declaration, l’espace de noms interne devient membre de l’espace de noms externe. Dans les deux cas, le nom d’un espace de noms doit être unique dans l’espace de noms conteneur.

Les espaces de noms sont implicitement public et la déclaration d’un espace de noms ne peut pas inclure de modificateurs d’accès.

Dans un namespace_body, les using_directivefacultatifs importent les noms d’autres espaces de noms, types et membres, ce qui leur permet d’être référencés directement au lieu d’utiliser des noms qualifiés. Les namespace_member_declarationfacultatifs contribuent aux membres à l’espace de déclaration de l’espace de noms. Notez que tous les using_directivedoivent apparaître avant toutes les déclarations de membre.

L’qualified_identifier d’un namespace_declaration peut être un identificateur unique ou une séquence d’identificateurs séparés par des jetons «. ». Ce dernier formulaire permet à un programme de définir un espace de noms imbriqué sans imbrication lexicale de plusieurs déclarations d’espace de noms.

Exemple :

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

est sémantiquement équivalent à

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

exemple de fin

Les espaces de noms sont ouverts et deux déclarations d’espace de noms portant le même nom complet (§7.8.2) contribuent au même espace de déclaration (§7.3).

Exemple : dans le code suivant

namespace N1.N2
{
    class A {}
}

namespace N1.N2
{
    class B {}
}

les deux déclarations d’espace de noms ci-dessus contribuent au même espace de déclaration, dans ce cas en déclarant deux classes avec les noms qualifiés complets N1.N2.A et N1.N2.B. Étant donné que les deux déclarations contribuent à la même espace de déclaration, il aurait été une erreur si chacune contenait une déclaration d’un membre portant le même nom.

exemple de fin

14.4 Directives d’alias extern

Une extern_alias_directive introduit un identificateur qui sert d’alias pour un espace de noms. La spécification de l’espace de noms alias est externe au code source du programme et s’applique également aux espaces de noms imbriqués de l’espace de noms alias.

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

L’étendue d’une extern_alias_directive s’étend sur les using_directive, les global_attributes et les namespace_member_declarationde ses compilation_unit ou namespace_body immédiatement.

Dans un corps d’unité de compilation ou d’espace de noms qui contient un extern_alias_directive, l’identificateur introduit par l’extern_alias_directive peut être utilisé pour référencer l’espace de noms alias. Il s’agit d’une erreur au moment de la compilation pour que l’identificateur soit le mot global.

L’alias introduit par un extern_alias_directive est très similaire à l’alias introduit par un using_alias_directive. Consultez le §14.5.2 pour plus d’informations sur les extern_alias_directiveet les using_alias_directive.

alias est un mot clé contextuel (§6.4.4) et n’a qu’une signification particulière lorsqu’il suit immédiatement le extern mot clé dans un extern_alias_directive.

Une erreur se produit si un programme déclare un alias extern pour lequel aucune définition externe n’est fournie.

Exemple : le programme suivant déclare et utilise deux alias extern et X Y, chacun représentant la racine d’une hiérarchie d’espaces de noms distincte :

extern alias X;
extern alias Y;

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

Le programme déclare l’existence des alias X extern et Y, mais les définitions réelles des alias sont externes au programme. Les classes identiques nommées N.B peuvent désormais être référencées en tant que etY.N.BX.N.B, ou, à l’aide du qualificateur d’alias d’espace de noms, X::N.B et Y::N.B. exemple de fin

14.5 Utilisation de directives

14.5.1 Général

L’utilisation de directives facilite l’utilisation d’espaces de noms et de types définis dans d’autres espaces de noms. L’utilisation de directives affecte le processus de résolution de noms des namespace_or_type_name (§7.8) et des simple_name(§12.8.4), mais contrairement aux déclarations, les using_directivene contribuent pas aux nouveaux membres aux espaces de déclaration sous-jacents des unités de compilation ou des espaces de noms dans lesquels ils sont utilisés.

using_directive
    : using_alias_directive
    | using_namespace_directive
    | using_static_directive    
    ;

Un using_alias_directive (§14.5.2) introduit un alias pour un espace de noms ou un type.

Un using_namespace_directive (§14.5.3) importe les membres de type d’un espace de noms.

Un using_static_directive (§14.5.4) importe les types imbriqués et les membres statiques d’un type.

L’étendue d’une using_directive s’étend sur la namespace_member_declarations de son corps d’unité de compilation ou d’espace de noms immédiatement. L’étendue d’une using_directive n’inclut pas spécifiquement ses using_directivehomologues. Ainsi, les using_directivehomologues n’affectent pas les uns les autres, et l’ordre dans lequel ils sont écrits est insignifiant. En revanche, l’étendue d’un extern_alias_directive inclut les using_directivedéfinies dans le même corps d’unité de compilation ou d’espace de noms.

14.5.2 Utilisation des directives d’alias

Un using_alias_directive introduit un identificateur qui sert d’alias pour un espace de noms ou un type dans l’unité de compilation ou le corps de l’espace de noms immédiatement englobant.

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

Dans les attributs globaux et les déclarations de membre dans un corps d’unité de compilation ou d’espace de noms qui contient un using_alias_directive, l’identificateur introduit par l’using_alias_directive peut être utilisé pour référencer l’espace de noms ou le type donné.

Exemple :

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

    class B: A {}
}

Ci-dessus, dans les déclarations de membre dans l’espace N3 de noms, A est un alias pour N1.N2.A, et donc la classe N3.B dérive de la classe N1.N2.A. Le même effet peut être obtenu en créant un alias R pour N1.N2 et en référençant R.A:

namespace N3
{
    using R = N1.N2;

    class B : R.A {}
}

exemple de fin

Dans l’utilisation de directives, d’attributs globaux et de déclarations de membres dans un corps d’unité de compilation ou d’espace de noms qui contient un extern_alias_directive, l’identificateur introduit par l’extern_alias_directive peut être utilisé pour référencer l’espace de noms associé.

Exemple : Par exemple :

namespace N1
{
    extern alias N2;

    class B : N2::A {}
}

Au-dessus, dans les déclarations de membre de l’espace N1 de noms, N2 il s’agit d’un alias pour un espace de noms dont la définition est externe au code source du programme. La classe N1.B dérive de la classe N2.A. Le même effet peut être obtenu en créant un alias A pour N2.A et en référençant A:

namespace N1
{
    extern alias N2;

    using A = N2::A;

    class B : A {}
}

exemple de fin

Un extern_alias_directive ou using_alias_directive rend un alias disponible dans un corps d’unité de compilation ou d’espace de noms particulier, mais il ne contribue pas aux nouveaux membres à l’espace de déclaration sous-jacent. En d’autres termes, une directive d’alias n’est pas transitive, mais affecte uniquement l’unité de compilation ou le corps de l’espace de noms dans lequel il se produit.

Exemple : dans le code suivant

namespace N3
{
    extern alias R1;

    using R2 = N1.N2;
}

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

les étendues des directives d’alias qui introduisent R1 et R2 s’étendent uniquement aux déclarations de membre dans le corps de l’espace de noms dans lequel elles sont contenues, de sorte qu’elles R1 R2 sont inconnues dans la deuxième déclaration d’espace de noms. Toutefois, le fait de placer les directives d’alias dans l’unité de compilation contenant entraîne la disponibilité de l’alias dans les deux déclarations d’espace de noms :

extern alias R1;

using R2 = N1.N2;

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

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

exemple de fin

Chaque extern_alias_directive ou using_alias_directive dans un compilation_unit ou namespace_body contribue à un nom à l’espace de déclaration d’alias (§7.3) du compilation_unit ou namespace_body immédiatement englobant. L’identificateur de la directive d’alias doit être unique dans l’espace de déclaration d’alias correspondant. L’identificateur d’alias n’a pas besoin d’être unique dans l’espace de déclaration global ou dans l’espace de déclaration de l’espace de noms correspondant.

Exemple :

extern alias X;
extern alias Y;

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

class Y {} // Ok

L’alias utilisant nommé X provoque une erreur, car il existe déjà un alias nommé X dans la même unité de compilation. La classe nommée Y n’est pas en conflit avec l’alias extern nommé Y , car ces noms sont ajoutés à des espaces de déclaration distincts. L’ancien est ajouté à l’espace de déclaration globale et celui-ci est ajouté à l’espace de déclaration d’alias pour cette unité de compilation.

Lorsqu’un nom d’alias correspond au nom d’un membre d’un espace de noms, l’utilisation de l’un ou l’autre doit être qualifiée de manière appropriée :

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
}

Dans le deuxième corps de l’espace de noms pour N3, l’utilisation non qualifiée des B résultats entraîne une erreur, car N3 contient un membre nommé B et le corps de l’espace de noms qui déclare également un alias avec un nom B; de même pour A. La classe N3.B peut être référencée en tant que N3.B ou global::N3.B. L’alias A peut être utilisé dans un membre qualifié-alias (§14.8), tel que A::B. L’alias B est essentiellement inutile. Il ne peut pas être utilisé dans un qualified_alias_member , car seuls les alias d’espace de noms peuvent être utilisés dans un qualified_alias_member et B les alias d’un type.

exemple de fin

Tout comme les membres réguliers, les noms introduits par alias_directives sont masqués par des membres nommés de la même façon dans les étendues imbriquées.

Exemple : dans le code suivant

using R = N1.N2;

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

la référence à R.A dans la déclaration de causes d’une erreur au moment de B la compilation, car R fait référence à N3.R, et non N1.N2.

exemple de fin

L’ordre dans lequel les extern_alias_directivesont écrits n’a aucune importance. De même, l’ordre dans lequel les using_alias_directivesont écrits n’a aucune importance, mais toutes les using_alias_directives doivent venir après tous les extern_alias_directives dans le même corps d’unité de compilation ou d’espace de noms. La résolution de la namespace_or_type_name référencée par un using_alias_directive n’est pas affectée par le using_alias_directive lui-même ou par d’autres using_directives dans le corps de l’unité de compilation ou de l’espace de noms contenant immédiatement, mais peut être affectée par des extern_alias_directivedans le corps de l’unité de compilation ou de l’espace de noms contenant immédiatement. En d’autres termes, le namespace_or_type_name d’un using_alias_directive est résolu comme si le corps de l’unité de compilation ou de l’espace de noms contenant immédiatement n’avait pas de using_directives, mais qu’il a le jeu correct de extern_alias_directives.

Exemple : dans le code suivant

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
}

la dernière using_alias_directive génère une erreur au moment de la compilation, car elle n’est pas affectée par la using_alias_directive précédente. La première using_alias_directive n’entraîne pas d’erreur, car l’étendue de l’alias extern X inclut le using_alias_directive.

exemple de fin

Un using_alias_directive peut créer un alias pour n’importe quel espace de noms ou type, y compris l’espace de noms dans lequel il apparaît et n’importe quel espace de noms ou type imbriqué dans cet espace de noms.

L’accès à un espace de noms ou un type par le biais d’un alias génère exactement le même résultat que l’accès à cet espace de noms ou à ce type via son nom déclaré.

Exemple : Donné

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

les noms N1.N2.A, R1.N2.Aet sont équivalents et R2.A tous font référence à la déclaration de classe dont le nom complet est N1.N2.A.

exemple de fin

Bien que chaque partie d’un type partiel (§15.2.7) soit déclarée dans le même espace de noms, les parties sont généralement écrites dans différentes déclarations d’espace de noms. Ainsi, différents extern_alias_directives et using_directivepeuvent être présents pour chaque partie. Lors de l’interprétation de noms simples (§12.8.4) dans une partie, seuls les extern_alias_directive s et les using_directivedes corps d’espace de noms et de l’unité de compilation englobant cette partie sont considérés. Cela peut entraîner le même identificateur ayant des significations différentes dans différentes parties.

Exemple :

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

exemple de fin

L’utilisation d’alias peut nommer un type construit fermé, mais ne peut pas nommer une déclaration de type générique sans fournir d’arguments de type.

Exemple :

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
}

exemple de fin

14.5.3 Utilisation des directives d’espace de noms

Une using_namespace_directive importe les types contenus dans un espace de noms dans le corps de l’unité de compilation ou de l’espace de noms englobant immédiatement, ce qui permet à l’identificateur de chaque type d’être utilisé sans qualification.

using_namespace_directive
    : 'using' namespace_name ';'
    ;

Dans les déclarations de membre d’une unité de compilation ou d’un corps d’espace de noms qui contient un using_namespace_directive, les types contenus dans l’espace de noms donné peuvent être référencés directement.

Exemple :

namespace N1.N2
{
    class A {}
}

namespace N3
{
    using N1.N2;

    class B : A {}
}

Au-dessus, dans les déclarations de membre dans l’espace N3 de noms, les membres de type sont N1.N2 directement disponibles, et par conséquent, la classe N3.B dérive de la classe N1.N2.A.

exemple de fin

Une using_namespace_directive importe les types contenus dans l’espace de noms donné, mais n’importe pas spécifiquement les espaces de noms imbriqués.

Exemple : dans le code suivant

namespace N1.N2
{
    class A {}
}

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

l’using_namespace_directive importe les types contenus dans N1, mais pas les espaces de noms imbriqués dans N1. Par conséquent, la référence à N2.A la déclaration des résultats d’une erreur au moment de B la compilation, car aucun membre nommé N2 n’est dans l’étendue.

exemple de fin

Contrairement à un using_alias_directive, un using_namespace_directive peut importer des types dont les identificateurs sont déjà définis dans l’unité de compilation englobante ou le corps de l’espace de noms. En effet, les noms importés par un using_namespace_directive sont masqués par des membres nommés de la même façon dans l’unité de compilation englobante ou le corps de l’espace de noms.

Exemple :

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

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

Ici, dans les déclarations de membre dans l’espace N3 de noms, A fait référence au N3.A lieu de N1.N2.A.

exemple de fin

Étant donné que les noms peuvent être ambigus lorsque plusieurs espaces de noms importés introduisent le même nom de type, une using_alias_directive est utile pour lever l’ambiguïté de la référence.

Exemple : dans le code suivant

namespace N1
{
    class A {}
}

namespace N2
{
    class A {}
}

namespace N3
{
    using N1;
    using N2;

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

les deux N1 et N2 contiennent un membre A, et parce que N3 les importations à la fois, le A référencement est N3 une erreur au moment de la compilation. Dans cette situation, le conflit peut être résolu soit par la qualification des références à A, soit en introduisant une using_alias_directive qui choisit un particulier A. Par exemple :

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

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

exemple de fin

En outre, lorsque plusieurs espaces de noms ou types importés par using_namespace_directives ou using_static_directives dans le même corps d’unité de compilation ou d’espace de noms contiennent des types ou des membres du même nom, les références à ce nom en tant que simple_name sont considérées comme ambiguës.

Exemple :

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 contient un membre Ade type et C contient un champ Astatique, et parce que N2 les importations à la fois, le A référencement en tant que simple_name est ambigu et une erreur au moment de la compilation.

exemple de fin

Comme un using_alias_directive, un using_namespace_directive ne contribue pas aux nouveaux membres à l’espace de déclaration sous-jacent de l’unité de compilation ou de l’espace de noms, mais affecte uniquement l’unité de compilation ou le corps de l’espace de noms dans lequel il apparaît.

La namespace_name référencée par un using_namespace_directive est résolue de la même façon que la namespace_or_type_name référencée par un using_alias_directive. Par conséquent, les using_namespace_directivedans le même corps d’unité de compilation ou d’espace de noms n’affectent pas les uns les autres et peuvent être écrits dans n’importe quel ordre.

14.5.4 Utilisation de directives statiques

Une using_static_directive importe les types imbriqués et les membres statiques contenus directement dans une déclaration de type dans le corps de l’unité de compilation ou de l’espace de noms qui entoure immédiatement l’identificateur de chaque membre et type à utiliser sans qualification.

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

Dans les déclarations de membre d’une unité de compilation ou d’un corps d’espace de noms qui contient un using_static_directive, les types imbriqués accessibles et les membres statiques (à l’exception des méthodes d’extension) contenus directement dans la déclaration du type donné peuvent être référencés directement.

Exemple :

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

Dans le code précédent, dans les déclarations de membre dans l’espace N2 de noms, les membres statiques et les types imbriqués de N1.A sont directement disponibles, et ainsi la méthode N est en mesure de référencer à la fois les membres et M les B membres de N1.A.

exemple de fin

Une using_static_directive n’importe pas spécifiquement les méthodes d’extension directement en tant que méthodes statiques, mais les rend disponibles pour l’appel de méthode d’extension (§12.8.9.3).

Exemple :

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

la using_static_directive importe la méthode M d’extension contenue dans N1.A, mais uniquement en tant que méthode d’extension. Par conséquent, la première référence au M corps des résultats d’une erreur au moment de B.N la compilation, car aucun membre nommé M n’est dans l’étendue.

exemple de fin

Une using_static_directive importe uniquement les membres et les types déclarés directement dans le type donné, et non les membres et les types déclarés dans les classes de base.

Exemple :

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

la using_static_directive importe la méthode M2 contenue dans N1.B, mais n’importe pas la méthode M contenue dans N1.A. Par conséquent, la référence au M corps des résultats d’une erreur au moment de C.N la compilation, car aucun membre nommé M n’est dans l’étendue. Les développeurs doivent ajouter une deuxième using static directive pour spécifier que les méthodes dans N1.A devront également être importées.

exemple de fin

Les ambiguïtés entre plusieurs using_namespace_directives et using_static_directives sont abordées dans le §14.5.3.

14.6 Déclarations de membre d’espace de noms

Un namespace_member_declaration est un namespace_declaration (§14.3) ou un type_declaration (§14.7).

namespace_member_declaration
    : namespace_declaration
    | type_declaration
    ;

Une unité de compilation ou un corps d’espace de noms peut contenir des namespace_member_declaration, et ces déclarations contribuent aux nouveaux membres à l’espace de déclaration sous-jacent de l’unité de compilation ou du corps de l’espace de noms contenant.

14.7 Déclarations de type

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

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

Une type_declaration peut se produire en tant que déclaration de niveau supérieur dans une unité de compilation ou en tant que déclaration membre dans un espace de noms, une classe ou un struct.

Lorsqu’une déclaration de type pour un type T se produit en tant que déclaration de niveau supérieur dans une unité de compilation, le nom complet (§7.8.2) de la déclaration de type est identique au nom non qualifié de la déclaration (§7.8.2). Lorsqu’une déclaration de type pour un type T se produit dans une déclaration d’espace de noms, de classe ou de struct, le nom complet (§7.8.3) de la déclaration S.Nde type, où S est le nom complet de l’espace de noms, de la classe ou de la déclaration de struct contenant, et N est le nom non qualifié de la déclaration.

Un type déclaré dans une classe ou un struct est appelé type imbriqué (§15.3.9).

Les modificateurs d’accès autorisés et l’accès par défaut pour une déclaration de type dépendent du contexte dans lequel la déclaration a lieu (§7.5.2) :

  • Les types déclarés dans les unités de compilation ou les espaces de noms peuvent avoir public ou internal accéder. La valeur par défaut est l’accès internal .
  • Les types déclarés dans les classes peuvent avoir public, , protected internalprotected, private protected, internalou private accéder. La valeur par défaut est l’accès private .
  • Les types déclarés dans les structs peuvent avoir public, internalou private accéder. La valeur par défaut est l’accès private .

14.8 Membre d’alias qualifié

14.8.1 Général

Le qualificateur :: d’alias d’espace de noms permet de garantir que les recherches de noms de type ne sont pas affectées par l’introduction de nouveaux types et membres. Le qualificateur d’alias d’espace de noms apparaît toujours entre deux identificateurs appelés identificateurs de gauche et de droite. Contrairement au qualificateur normal . , l’identificateur de gauche du :: qualificateur est recherché uniquement comme un extern ou en utilisant un alias.

Un qualified_alias_member fournit un accès explicite à l’espace de noms global et à l’extern ou à l’utilisation d’alias potentiellement masqués par d’autres entités.

qualified_alias_member
    : identifier '::' identifier type_argument_list?
    ;

Un qualified_alias_member peut être utilisé comme namespace_or_type_name (§7.8) ou comme opérande gauche dans un member_access (§12.8.7).

Un qualified_alias_member se compose de deux identificateurs, appelés identificateurs de gauche et de droite, séparés par le :: jeton et éventuellement suivis d’un type_argument_list. Lorsque l’identificateur de gauche est global, l’espace de noms global est recherché pour l’identificateur de droite. Pour tout autre identificateur de gauche, cet identificateur est recherché sous la forme d’un extern ou d’un alias (§14.4 et §14.5.2). Une erreur au moment de la compilation se produit s’il n’existe pas d’alias de ce type ou si l’alias fait référence à un type. Si l’alias fait référence à un espace de noms, cet espace de noms est recherché pour l’identificateur de droite.

Un qualified_alias_member a l’une des deux formes suivantes :

  • N::I<A₁, ..., Aₑ>, où N et I représentent des identificateurs, et <A₁, ..., Aₑ> est une liste d’arguments de type. (e est toujours au moins un.)
  • N::I, où N et I représentent des identificateurs. (Dans ce cas, e est considéré comme égal à zéro.)

À l’aide de cette notation, la signification d’une qualified_alias_member est déterminée comme suit :

  • S’il N s’agit de l’identificateurglobal, l’espace de noms global est recherché :I
    • Si l’espace de noms global contient un espace de noms nommé I et e est égal à zéro, la qualified_alias_member fait référence à cet espace de noms.
    • Sinon, si l’espace de noms global contient un type non générique nommé I et e est égal à zéro, le qualified_alias_member fait référence à ce type.
    • Sinon, si l’espace de noms global contient un type nommé I qui a e des paramètres de type, l’qualified_alias_member fait référence à ce type construit avec les arguments de type donnés.
    • Sinon, le qualified_alias_member n’est pas défini et une erreur au moment de la compilation se produit.
  • Sinon, à partir de la déclaration d’espace de noms (§14.3) contenant immédiatement la qualified_alias_member (le cas échéant), en continuant avec chaque déclaration d’espace de noms englobante (le cas échéant) et en se terminant par l’unité de compilation contenant le qualified_alias_member, les étapes suivantes sont évaluées jusqu’à ce qu’une entité se trouve :
    • Si l’unité de déclaration ou de compilation d’espace de noms contient un using_alias_directive qui associe N à un type, le qualified_alias_member n’est pas défini et une erreur au moment de la compilation se produit.
    • Sinon, si la déclaration d’espace de noms ou l’unité de compilation contient une extern_alias_directive ou using_alias_directive qui s’associe N à un espace de noms, puis :
      • Si l’espace de noms associé à N un espace de noms contient un espace de noms nommé I et e est égal à zéro, la qualified_alias_member fait référence à cet espace de noms.
      • Sinon, si l’espace de noms associé à N contient un type non générique nommé I et e est égal à zéro, la qualified_alias_member fait référence à ce type.
      • Sinon, si l’espace de noms associé à N contient un type nommé I qui a e des paramètres de type, l’qualified_alias_member fait référence à ce type construit avec les arguments de type donnés.
      • Sinon, le qualified_alias_member n’est pas défini et une erreur au moment de la compilation se produit.
  • Sinon, le qualified_alias_member n’est pas défini et une erreur au moment de la compilation se produit.

Exemple : Dans le 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;
    }
}

la classe A est référencée avec global::A et le type System.Net.Sockets.Socket est référencé avec S::Socket. L’utilisation A.x et S.Socket à la place aurait provoqué des erreurs au moment de la compilation, car A elles S auraient été résolues sur les paramètres.

exemple de fin

Remarque : L’identificateur global a une signification particulière uniquement lorsqu’il est utilisé comme identificateur de gauche d’un qualified_alias_name. Ce n’est pas un mot clé et il n’est pas lui-même un alias ; il s’agit d’un mot clé contextuel (§6.4.4). Dans le code :

class A { }

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

l’utilisation global.A provoque une erreur au moment de la compilation, car il n’existe aucune entité nommée global dans l’étendue. Si une entité nommée global était dans l’étendue, global elle global.A aurait été résolue sur cette entité.

L’utilisation global comme identificateur de gauche d’un qualified_alias_member provoque toujours une recherche dans l’espace global de noms, même s’il existe un alias utilisant nommé global. Dans le 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 résout MyGlobalTypes.A et global::A résout la classe A dans l’espace de noms global.

Note de fin

14.8.2 Unicité des alias

Chaque unité de compilation et corps de l’espace de noms possède un espace de déclaration distinct pour les alias extern et l’utilisation d’alias. Ainsi, alors que le nom d’un alias extern ou d’un alias d’utilisation doit être unique dans l’ensemble d’alias externs et en utilisant des alias déclarés dans le corps de l’unité de compilation ou de l’espace de noms immédiatement contenant, un alias est autorisé à avoir le même nom qu’un type ou un espace de noms tant qu’il est utilisé uniquement avec le :: qualificateur.

Exemple : Dans les éléments suivants :

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

le nom A a deux significations possibles dans le deuxième corps de l’espace de noms, car la classe A et l’alias A using sont dans l’étendue. Pour cette raison, l’utilisation du nom A.Stream qualifié est ambiguë et provoque une erreur au moment de A la compilation. Toutefois, l’utilisation :: du A qualificateur n’est pas une erreur, car A elle n’est recherchée qu’en tant qu’alias d’espace de noms.

exemple de fin