Dela via


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.

  1. 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.
  2. 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:
  • 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. Om K inte är noll tas alla medlemmar med ett annat antal typparametrar bort. När K ä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;
}