Fillokala typer
Not
Den här artikeln är en funktionsspecifikation. Specifikationen fungerar som designdokument för funktionen. Den innehåller föreslagna specifikationsändringar, tillsammans med information som behövs under utformningen och utvecklingen av funktionen. Dessa artiklar publiceras tills de föreslagna specifikationsändringarna har slutförts och införlivats i den aktuella ECMA-specifikationen.
Det kan finnas vissa skillnader mellan funktionsspecifikationen och den slutförda implementeringen. Dessa skillnader samlas in i de relevanta LDM-anteckningar (Language Design Meeting).
Du kan läsa mer om processen för att införa funktionsspecifikationer i C#-språkstandarden i artikeln om specifikationerna.
Champion-problem: https://github.com/dotnet/csharplang/issues/5529
Sammanfattning
Tillåt en file
modifierare för deklarationer av toppnivåtyp. Typen finns bara i filen där den deklareras.
// 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
Vår främsta motivation är från källgeneratorer. Källgeneratorer fungerar genom att lägga till filer i användarens kompilering.
- Dessa filer bör kunna innehålla implementeringsinformation som är dolda från resten av kompileringen, men som ändå kan användas i hela filen som de deklareras i.
- Vi vill minska behovet av att generatorer "söker" efter typnamn som inte kolliderar med deklarationer i användarkod eller kod från andra generatorer.
Detaljerad design
- Vi lägger till
file
-modifieraren i följande modifieraruppsätt:- klass
- struct
- gränssnitt
- enum
- delegera
- rekord
- rekordstruktur
- Den
file
modifieraren kan bara användas på en toppnivåtyp.
När en typ har file
modifierare sägs det vara en fillokal typ.
Tillgänglighet
Modifieraren file
klassificeras inte som en åtkomstmodifierare. Inga åtkomstmodifierare kan användas i kombination med file
på en datatyp.
file
behandlas som ett oberoende begrepp från tillgänglighet. Eftersom fillokala typer inte kan kapslas kan endast standardtillgänglighet internal
användas med file
typer.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Namngivning
Implementeringen garanterar att fillokala typer i olika filer med samma namn kommer att vara distinkta under körtid. Typens tillgänglighet och namn i metadata bestäms av implementationen. Avsikten är att tillåta kompilatorn att införa eventuella framtida funktioner för åtkomstbegränsning under körtiden som är lämpade för denna funktion. I den första implementeringen förväntas en internal
-tillgänglighet användas och ett obeskrivligt genererat namn kommer att användas, vilket är beroende av filen som typen är deklarerad i.
Sökning
Vi ändrar avsnittet medlemssökning på följande sätt (ny text i fetstil):
- Om
K
är noll tas sedan alla kapslade typer vars deklarationer innehåller typparametrar bort. OmK
inte är noll tas alla medlemmar med ett annat antal typparametrar bort. NärK
är noll tas metoder med typparametrar inte bort, eftersom typinferensprocessen (§11.6.3) kan härleda typargumenten.- Låt sedan F vara kompileringsenheten som innehåller uttrycket där medlemssökningen sker. Alla medlemmar som är typer lokala för filen och inte deklareras i F tas bort från uppsättningen.
- Nästa, om uppsättningen med tillgängliga medlemmar innehåller fillokala typer, tas alla medlemmar som inte är fillokala typer bort från uppsättningen.
Anmärkningar
Dessa regler tillåter inte användning av fillokala typer utanför filen där de deklareras.
Dessa regler tillåter också att en fillokal typ skugga ett namnområde eller en icke-fillokal typ:
// 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
}
}
Observera att vi inte uppdaterar omfångsavsnittet i specifikationen. Just där specificeras:
Det omfånget av ett namn är den region i programkoden där det är möjligt att referera till den entitet som deklarerats med namnet utan krav på kvalificering av namnet.
I själva verket påverkar omfånget endast sökningen av icke-kvalificerade namn. Det här är inte riktigt rätt koncept för oss att utnyttja eftersom vi också måste påverka sökningen av kvalificerade namn:
// 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
}
}
Därför anger vi inte funktionen när det gäller vilket omfång typen finns i, utan snarare som ytterligare "filtreringsregler" i medlemssökningen.
Attribut
Fillokala klasser tillåts vara attributtyper och kan användas som attribut inom både fillokala typer och icke-fillokala typer, precis som om attributtypen var en icke-fillokal typ. Metadatanamnet för den fillokala attributtypen går fortfarande igenom samma namngenereringsstrategi som andra fillokala typer. Det innebär att det sannolikt är opraktiskt att upptäcka förekomsten av en fillokal typ med ett hårdkodat strängnamn, eftersom det kräver att man är beroende av den interna namngenereringsstrategin hos kompilatorn, som kan ändras över tid. Det fungerar dock att identifiera via typeof(MyFileLocalAttribute)
.
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
}
}
Användning i signaturer
Det finns ett allmänt behov av att förhindra att fillokala typer visas i medlemsparametrar, returer och typparameterbegränsningar där den fillokala typen kanske inte finns i omfånget när medlemmen används.
Observera att icke-fillokala typer tillåts implementera fillokala gränssnitt, ungefär som hur typer kan implementera mindre tillgängliga gränssnitt. Beroende på vilka typer som finns i gränssnittsmedlemmarna kan det leda till ett brott mot reglerna i följande avsnitt.
Endast tillåta signaturanvändning i medlemmar av fillokala datatyper
Det kanske enklaste sättet att säkerställa detta är att framtvinga att fillokala typer endast får förekomma i signaturer eller som bastyper för andra fillokala typer:
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
}
Observera att detta begränsar användningen i explicita implementeringar, även om sådana användningar är säkra. Vi gör detta för att förenkla reglerna för den inledande iterationen av funktionen.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
Det är ett kompileringsfel att använda en fil-lokal typ i ett global using static
-direktiv, d.v.s.,
global using static C; // error
file class C
{
public static void M() { }
}
Implementering/överlagringar
Fil-lokala typdeklarationer kan implementera gränssnitt, åsidosätta virtuella metoder, och så vidare, precis som vanliga typdeklarationer.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications