Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
Questo articolo è una specifica di funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.
Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono acquisite nelle note
Puoi trovare maggiori informazioni sul processo per adottare gli speclet delle funzionalità nello standard del linguaggio C# nell'articolo sulle specifiche di .
Problema del campione: https://github.com/dotnet/csharplang/issues/5529
Sommario
Consentire un modificatore file nelle dichiarazioni di tipo di primo livello. Il tipo esiste solo nel file in cui è dichiarato.
// 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.
Motivazione
La nostra motivazione principale deriva da generatori di codice sorgente. I generatori di origine funzionano aggiungendo file alla compilazione dell'utente.
- Questi file devono essere in grado di contenere i dettagli di implementazione che sono nascosti dal resto della compilazione, ma sono utilizzabili in tutto il file in cui sono dichiarati.
- Si vuole ridurre la necessità per i generatori di "cercare" nomi di tipo che non conflittano con dichiarazioni nel codice dell'utente o nel codice di altri generatori.
Progettazione dettagliata
- Aggiungere il modificatore
fileai set di modifica seguenti:- classe
- struct
- interfaccia
- enumerazione
- delegato
- Registrazione
- Record struct.
- Il modificatore
filepuò essere usato solo su un tipo di livello superiore.
Quando un tipo ha il modificatore
Accessibilità
Il modificatore file non è classificato come modificatore di accessibilità. Non è possibile usare modificatori di accessibilità in combinazione con file su un tipo.
file viene considerato un concetto indipendente dall'accessibilità. Poiché i tipi locali di file non possono essere annidati, solo l'accessibilità predefinita internal è utilizzabile per i tipi file.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Denominazione
L'implementazione garantisce che i tipi locali di file in file diversi con lo stesso nome siano distinti per il runtime. L'accessibilità e il nome del tipo nei metadati sono definiti dall'implementazione. L'intenzione è consentire al compilatore di adottare eventuali funzionalità future di limitazione dell'accesso nel runtime adatte alla funzionalità. È previsto che nell'implementazione iniziale venga usata un'accessibilità internal e che venga usato un nome generato impronunciabile che dipende dal file in cui viene dichiarato il tipo.
Ricerca
Modifichiamo la sezione ricerca di membri come segue (nuovo testo in grassetto):
- Successivamente, se
Kè zero, vengono rimossi tutti i tipi annidati le cui dichiarazioni includono parametri di tipo. SeKnon è zero, vengono rimossi tutti i membri con un numero diverso di parametri di tipo. QuandoKè zero, i metodi con parametri di tipo non vengono rimossi, poiché il processo di inferenza del tipo (§11.6.3) potrebbe essere in grado di dedurre gli argomenti del tipo.- Lasciare quindi F essere l'unità di compilazione che contiene l'espressione in cui si sta verificando la ricerca dei membri. Tutti i membri che sono tipi locali di file e non vengono dichiarati in F vengono rimossi dal set.
- Avanti, se il set di membri accessibili contiene tipi locali di file, tutti i membri che non sono tipi file-local vengono rimossi dal set.
Osservazioni
Queste regole non consentono l'utilizzo di tipi locali di file all'esterno del file in cui sono dichiarati.
Queste regole consentono anche a un tipo locale al file di oscurare uno spazio dei nomi o un tipo di file non locale:
// 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
}
}
Si noti che non vengono aggiornati gli ambiti della sezione della specifica. Questo perché, come afferma la specifica:
L'ambito di un nome è l'area del testo del programma all'interno della quale è possibile fare riferimento all'entità dichiarata dal nome senza qualificare il nome.
In effetti, l'ambito influisce solo sulla ricerca di nomi non qualificati. Questo non è il concetto giusto da sfruttare perché dobbiamo anche influire sulla ricerca dei nomi qualificati:
// 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
}
}
Pertanto, non si specifica la funzionalità in termini di ambito in cui il tipo è contenuto, ma piuttosto come "regole di filtro aggiuntive" nella ricerca dei membri.
Attributi
Le classi locali al file possono essere tipi di attributi e possono essere utilizzate come attributi sia all'interno di tipi locali al file sia in tipi non locali al file, esattamente come se il tipo di attributo fosse un tipo non locale al file. Il nome del metadato del tipo di attributo file-locale passa comunque attraverso la stessa strategia di generazione degli altri tipi file-locali. Ciò significa che il rilevamento della presenza di un tipo locale di file tramite un nome stringa hard-coded è probabilmente poco pratico, perché richiede di dipendere dalla strategia di generazione dei nomi interna del compilatore, che può cambiare nel tempo. Tuttavia, il rilevamento tramite typeof(MyFileLocalAttribute) funziona.
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
}
}
Utilizzo nelle firme
È necessario impedire che i tipi locali di file vengano visualizzati nei parametri dei membri, nei valori di ritorno e nei vincoli di parametro di tipo in cui il tipo locale del file potrebbe non essere nello scope nel punto di utilizzo del membro.
Si noti che i tipi non locali di file sono autorizzati a implementare interfacce file-local, analogamente a come i tipi possono implementare interfacce meno accessibili. A seconda dei tipi presenti nei membri dell'interfaccia, potrebbe verificarsi una violazione delle regole nella sezione seguente.
Consentire solo l'utilizzo della firma nei membri dei tipi locali di file
Forse il modo più semplice per assicurarsi che questo sia quello di applicare che i tipi file-local possono essere visualizzati solo nelle firme o come tipi di base di altri tipi locali di file:
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
}
Si noti che ciò limita l'utilizzo nelle implementazioni esplicite, anche se tali utilizzi sono sicuri. Questa operazione viene eseguita per semplificare le regole per l'iterazione iniziale della funzionalità.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
È un errore in fase di compilazione utilizzare un tipo locale di file in una direttiva global using static, cioè
global using static C; // error
file class C
{
public static void M() { }
}
Implementazione/sovrascritture
Le dichiarazioni di tipi locali di file possono implementare interfacce, eseguire l'override di metodi virtuali e così via, proprio come le dichiarazioni di tipo regolare.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications