Delen via


Bestandstypen

Notitie

Dit artikel is een functiespecificatie. De specificatie fungeert als het ontwerpdocument voor de functie. Het bevat voorgestelde specificatiewijzigingen, samen met informatie die nodig is tijdens het ontwerp en de ontwikkeling van de functie. Deze artikelen worden gepubliceerd totdat de voorgestelde specificaties zijn voltooid en opgenomen in de huidige ECMA-specificatie.

Er kunnen enkele verschillen zijn tussen de functiespecificatie en de voltooide implementatie. Deze verschillen worden vastgelegd in de relevante notities van de Language Design Meeting ().

Meer informatie over het proces voor het aannemen van functiespeclets in de C#-taalstandaard vindt u in het artikel over de specificaties.

Probleem met kampioen: https://github.com/dotnet/csharplang/issues/5529

Samenvatting

Hiermee staat u een file modifier toe op declaraties op het hoogste niveau. Het type bestaat alleen in het bestand waarin het is gedeclareerd.

// 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.

Motivatie

Onze primaire motivatie komt van brongeneratoren. Brongeneratoren werken door bestanden toe te voegen aan de compilatie van de gebruiker.

  1. Deze bestanden moeten implementatiedetails kunnen bevatten die verborgen zijn voor de rest van de compilatie, maar die kunnen worden gebruikt in het bestand waarin ze zijn gedeclareerd.
  2. We willen de noodzaak voor generatoren verminderen om te zoeken naar typenamen die niet botsen met declaraties in gebruikerscode of code van andere generatoren.

Gedetailleerd ontwerp

  • We voegen een file-modificator toe aan de volgende wijzigingssets:
  • De file modifier kan alleen worden gebruikt voor een type op het hoogste niveau.

Wanneer een type de file modifier heeft, wordt het een bestand-lokaal type.

Toegankelijkheid

De file modifier wordt niet geclassificeerd als een toegankelijkheidsaanpassingsfunctie. Er kunnen geen toegankelijkheidsmodificatoren worden gebruikt in combinatie met file op een type. file wordt beschouwd als een onafhankelijk concept van toegankelijkheid. Omdat bestandslokale typen niet kunnen worden genest, is standaardtoegankelijkheids-internal alleen bruikbaar met file typen.

public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok

Naamgeving

De implementatie garandeert dat bestands-lokale typen in verschillende bestanden met dezelfde naam uniek zijn voor de runtime. De toegankelijkheid en naam van het type in metagegevens zijn gedefinieerd door de implementatie. Het is de bedoeling dat de compiler toekomstige functies voor toegangsbeperkingen in de runtime kan gebruiken die geschikt zijn voor de functie. Er wordt verwacht dat in de eerste implementatie een internal toegankelijkheid gebruikt wordt en dat er een onuitsprekelijke gegenereerde naam gebruikt wordt, die afhankelijk is van het bestand waarin het type gedeclareerd is.

Opzoeken

We wijzigen de sectie als volgt (nieuwe tekst in vet):

  • Als K nul is, worden alle geneste typen waarvan de declaraties typeparameters bevatten, verwijderd. Als K niet nul is, worden alle leden met een ander aantal typeparameters verwijderd. Wanneer K nul is, worden methoden met typeparameters niet verwijderd, omdat het typedeductieproces (ยง11.6.3) mogelijk de typeargumenten kan afleiden.
  • Laat vervolgens F de compilatie-eenheid zijn die de expressie bevat waarin het opzoeken van leden plaatsvindt. Alle leden die bestands-lokale typen zijn en die niet zijn gedeclareerd in F- worden uit de set verwijderd.
  • Als de set toegankelijke leden bestand-lokale typen bevat, worden alle leden die geen bestand-lokale typen zijn uit de set verwijderd.

Opmerkingen

Deze regels staan het gebruik van lokale bestandstypen buiten het bestand waarin ze worden gedeclareerd, niet toe.

Met deze regels kan een lokaal bestandstype ook schaduwen over een naamruimte of een type dat niet lokaal is voor het bestand.

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

Houd er rekening mee dat we de bereiken niet bijwerken sectie van de specificatie. Dit komt omdat, zoals in de specificatie wordt aangegeven:

Het bereik van een naam is het gedeelte van de programmatekst waarin het mogelijk is om te verwijzen naar de entiteit die is gedeclareerd door de naam zonder nader specificeren van de naam.

Het bereik heeft in feite alleen invloed op het opzoeken van niet-gekwalificeerde namen. Dit is niet helemaal het juiste concept dat we kunnen gebruiken, omdat we ook van invloed moeten zijn op het opzoeken van gekwalificeerde namen:

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

Daarom specificeren we het kenmerk niet op basis van het bereik waarin het type is opgenomen, maar eerder als aanvullende "filterregels" bij het zoeken naar leden.

Kenmerken

Bestands-lokale klassen mogen kenmerktypen zijn en kunnen worden gebruikt als kenmerken binnen zowel bestands-lokale typen als niet-bestands-lokale typen, net alsof het kenmerktype een niet-bestand-lokaal type is. De naam van de metagegevens van het bestand-lokaal kenmerktype doorloopt nog steeds dezelfde strategie voor het genereren van namen als andere bestand-lokale typen. Dit betekent dat het detecteren van de aanwezigheid van een lokaal bestandstype door een in code vastgelegde tekenreeksnaam waarschijnlijk onpraktisch is, omdat dit vereist is, afhankelijk van de strategie voor het genereren van interne namen van de compiler, die na verloop van tijd kan veranderen. Detectie via typeof(MyFileLocalAttribute) werkt echter wel.

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

Gebruik in handtekeningen

Er is een algemene noodzaak om te voorkomen dat bestand-lokale typen worden weergegeven in lidparameters, retourwaarden en typeparameterbeperkingen, waarbij het bestand-lokale type mogelijk niet binnen het bereik valt op het moment van gebruik van het lid.

Houd er rekening mee dat niet-bestands-lokale typen zijn toegestaan om bestands-lokale interfaces te implementeren, vergelijkbaar met hoe typen minder toegankelijke interfaces kunnen implementeren. Afhankelijk van de typen die aanwezig zijn in de interfaceleden, kan dit leiden tot een schending van de regels in de volgende sectie.

Sta het gebruik van handtekeningen alleen toe in leden van typen die lokaal voor het bestand zijn.

Misschien is de eenvoudigste manier om ervoor te zorgen dat lokale bestandstypen alleen in handtekeningen of als basistypen van andere bestandstypen kunnen worden weergegeven:

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
}

Houd er rekening mee dat dit het gebruik in expliciete implementaties beperkt, ook al zijn dergelijke gebruiksrechten veilig. We doen dit om de regels voor de eerste iteratie van de functie te vereenvoudigen.

file interface I
{
    void M(I i);
}

class C : I
{
    void I.M(I i) { } // error
}

global using static

Het is een compilatiefout om een bestandslokaal type te gebruiken in een global using static-instructie, d.w.z.

global using static C; // error

file class C
{
    public static void M() { }
}

Implementatie/overschrijvingen

Declaraties van lokale bestandstypen kunnen interfaces implementeren, virtuele methoden overschrijven, enzovoort, net als gewone typedeclaraties.

file struct Widget : IEquatable<Widget>
{
    public bool Equals(Widget other) => true;
}