Types locaux de fichiers
Remarque
Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.
Il peut y avoir des différences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont consignées dans les notes pertinentes de la réunion de conception linguistique (LDM).
Pour en savoir plus sur le processus d'adoption des speclets de fonctionnalité dans la norme du langage C#, consultez l'article sur les spécifications.
Problème de champion : https://github.com/dotnet/csharplang/issues/5529
Récapitulatif
Autorisez un modificateur file
au niveau des déclarations de type de niveau supérieur. Le type existe uniquement dans le fichier dans lequel il est déclaré.
// File1.cs
namespace NS;
file class Widget
{
}
// File2.cs
namespace NS;
file class Widget // different symbol than the Widget in File1
{
}
// File3.cs
using NS;
var widget = new Widget(); // error: The type or namespace name 'Widget' could not be found.
Motivation
Notre principale motivation provient des générateurs sources. Les générateurs sources fonctionnent en ajoutant des fichiers à la compilation de l’utilisateur.
- Ces fichiers doivent être en mesure de contenir des détails d’implémentation qui sont masqués du reste de la compilation, mais qui sont utilisables dans tout le fichier dans lequel ils sont déclarés.
- Nous voulons éviter que les générateurs aient à « rechercher » les noms de types qui ne sont pas en conflit avec les déclarations dans le code utilisateur ou le code d’autres générateurs.
Conception détaillée
- Nous ajoutons le modificateur
file
aux jeux de modificateurs suivants : - Le modificateur
file
ne peut être utilisé qu’au niveau d’un type de niveau supérieur.
Lorsqu’un type a le modificateur file
, il est dit qu’il s’agit d’un type local au fichier .
Accessibilité
Le modificateur file
n’est pas classé comme modificateur d’accessibilité. Aucun modificateur d’accessibilité ne peut être utilisé en combinaison avec file
au niveau d’un type. file
est considéré comme un concept indépendant de l’accessibilité. Étant donné que les types locaux de fichiers ne peuvent pas être imbriqués, seuls les types avec accessibilité par défaut internal
sont utilisables avec les types file
.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Affectation de noms
L’implémentation garantit que les types locaux de fichiers dans différents fichiers portant le même nom seront distincts du runtime. L’accessibilité et le nom du type dans les métadonnées sont définis par l’implémentation. L’intention est de permettre au compilateur d’adopter les futures fonctionnalités de limitation d’accès dans le runtime, qui seront adaptées à cette fonctionnalité. Il est attendu que dans la mise en œuvre initiale, une accessibilité internal
soit utilisée et qu’un nom généré indescriptible soit utilisé, ce qui dépend du fichier dans lequel le type est déclaré.
Lookup
Nous modifions la section de recherche de membre comme suit (texte nouveau en gras ) :
- Ensuite, si
K
est égal à zéro, tous les types imbriqués dont les déclarations incluent des paramètres de type sont supprimés. SiK
n’est pas égal à zéro, tous les membres avec un nombre différent de paramètres de type sont supprimés. LorsqueK
est égal à zéro, les méthodes possédant des paramètres de type ne sont pas supprimées, puisque le processus d’inférence de type (§11.6.3) pourrait être en mesure d’inférer les arguments de type.- Ensuite, laissez F être l’unité de compilation qui contient l’expression où la recherche de membre aura lieu. Tous les membres qui sont des types locaux de fichiers et qui ne sont pas déclarés dans F seront supprimés de l’ensemble.
- Ensuite, si l’ensemble de membres accessibles contient des types locaux de fichiers, tous les membres qui ne sont pas des types locaux de fichiers sont supprimés de l’ensemble.
Remarques
Ces règles interdisent l’utilisation de types locaux de fichiers en dehors du fichier dans lequel elles sont déclarées.
Ces règles permettent également à un type local au fichier de shadow un espace de noms ou à un type non local au fichier :
// File1.cs
class C
{
public static void M() { }
}
// File2.cs
file class C
{
public static void M() { }
}
class Program
{
static void Main()
{
C.M(); // refers to the 'C' in File2.cs
}
}
Notez que nous ne mettons pas à jour la section étendue de la spécification. Cela est dû au fait que, comme la spécification le déclare :
L’étendue d’un nom est la région du texte du programme dans laquelle il est possible de renvoyer vers l’entité déclarée par le nom sans qualification du nom.
En effet, l’étendue n’impacte que la recherche de noms non qualifiés. Ce n’est pas tout à fait le bon concept à exploiter pour nous, car nous devons également influencer la recherche de noms qualifiés.
// File1.cs
namespace NS1
{
file class C
{
public static void M() { }
}
}
namespace NS2
{
class Program
{
public static void M()
{
C.M(); // error: C is not in scope
NS1.C.M(); // ok: C can be accessed through NS1.
}
}
}
// File2.cs
namespace NS1
{
class Program
{
C.M(); // error
NS1.C.M(); // error
}
}
Par conséquent, nous ne spécifions pas la fonctionnalité en termes d’étendue dans laquelle le type est contenu, mais plutôt comme des « filtering rules » supplémentaires dans la recherche de membres.
Attributs
Les classes locales de fichiers sont autorisées à être des types d’attributs et peuvent être utilisées comme attributs dans les types locaux de fichiers et les types non locaux aux fichiers, comme si le type d’attribut était un type non local au fichier. Le nom des métadonnées du type d’attribut local de fichier passe toujours par la même stratégie de génération de noms que les autres types locaux de fichiers. Cela signifie que détecter la présence d’un type local de fichier à l’aide d’un nom de chaîne codé en dur est susceptible d’être impraticable, car cela nécessite de dépendre de la stratégie de génération de noms interne du compilateur, qui peut changer au fil du temps. Toutefois, la détection via typeof(MyFileLocalAttribute)
fonctionne.
using System;
using System.Linq;
file class MyFileLocalAttribute : Attribute { }
[MyFileLocalAttribute]
public class C
{
public static void Main()
{
var attribute = typeof(C).CustomAttributes.Where(attr => attr.AttributeType == typeof(MyFileLocalAttribute)).First();
Console.Write(attribute); // outputs the generated name of the file-local attribute type
}
}
Utilisation dans les signatures
Il est généralement nécessaire d’empêcher les types locaux au fichier d’apparaître dans les paramètres des membres, les valeurs de retour et les contraintes de paramètres de type, là où le type local au fichier pourrait ne pas être dans le champ de visibilité au moment de l’utilisation du membre.
Notez que les types non locaux aux fichiers sont autorisés à implémenter des interfaces locales de fichiers, tout comme les types peuvent implémenter des interfaces moins accessibles. Selon les types présents dans les membres de l’interface, cela peut entraîner une violation des règles dans la section suivante.
Autoriser uniquement l’utilisation de signatures dans les membres des types locaux au fichier
Peut-être le moyen le plus simple de garantir que les types locaux au fichier n'apparaissent que dans les signatures ou en tant que types de base d'autres types locaux au fichier est de l'imposer.
file class FileBase
{
}
public class Derived : FileBase // error
{
private FileBase M2() => new FileBase() // error
}
file class FileDerived : FileBase // ok
{
private FileBase M2() => new FileBase(); // ok
}
Notez que cela limite l’utilisation dans les implémentations explicites, même si ces utilisations sont sûres. Nous procédons de la sorte pour simplifier les règles de l’itération initiale de la fonctionnalité.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
C’est une erreur de compilation d’utiliser un type local au fichier dans une directive global using static
, c’est-à-dire
global using static C; // error
file class C
{
public static void M() { }
}
Mise en œuvre/surcharges
Les déclarations de type local de fichier peuvent implémenter des interfaces, substituer des méthodes virtuelles, etc. tout comme les déclarations de type standard.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications