Typy lokalne plików
Notatka
Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.
Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są uwzględnione w odpowiednich notatkach ze spotkania projektowego języka (LDM).
Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .
Problem dotyczący mistrza: https://github.com/dotnet/csharplang/issues/5529
Streszczenie
Zezwolić na użycie modyfikatora file
w deklaracjach typów najwyższego poziomu. Typ istnieje tylko w pliku, w którym jest zadeklarowany.
// 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.
Motywacja
Naszą główną motywacją są generatory kodu źródłowego. Generatory źródeł działają, dodając pliki do kompilacji użytkownika.
- Pliki te powinny mieć możliwość przechowywania szczegółów implementacji, które są ukryte w pozostałej części kompilacji, ale mogą być używane w całym pliku, w którym są deklarowane.
- Chcemy zmniejszyć zapotrzebowanie generatorów na "wyszukiwanie" nazw typów, które nie będą zderzać się z deklaracjami w kodzie użytkownika lub kodzie z innych generatorów.
Szczegółowy projekt
- Dodamy modyfikator
file
do następujących zestawów modyfikatora:- Klasa
- , struktura
- interfejs
- wyliczenie
- delegowania
- rekord
- struktura rekord.
- Modyfikator
file
może być używany tylko w typie najwyższego poziomu.
Jeśli typ ma modyfikator file
, mówi się, że jest to typ pliku lokalnego.
Dostępność
Modyfikator file
nie jest klasyfikowany jako modyfikator ułatwień dostępu. Nie można używać modyfikatorów ułatwień dostępu w połączeniu z file
w typie.
file
jest traktowana jako niezależna koncepcja od ułatwień dostępu. Ponieważ typów lokalnych dla pliku nie można zagnieżdżać, z typami internal
można używać tylko domyślnej dostępności file
.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Nazewnictwo
Implementacja gwarantuje, że typy plików lokalnych w różnych plikach o tej samej nazwie będą unikatowe dla środowiska uruchomieniowego. Dostępność i nazwa typu w metadanych są zależne od implementacji. Celem jest zezwolenie kompilatorowi na przyjęcie wszelkich przyszłych funkcji ograniczeń dostępu w środowisku uruchomieniowym, które są odpowiednie dla tej funkcji. Oczekuje się, że w początkowej implementacji zostanie użyte ułatwienie dostępu internal
, a wygenerowana nazwa trudna do wypowiedzenia będzie zależeć od pliku, w którym zadeklarowany jest typ.
Wyszukiwanie
Zmieniamy sekcję wyszukiwania członków w następujący sposób (nowy tekst w pogrubiony):
- Następnie, jeśli
K
ma wartość zero, wszystkie zagnieżdżone typy, których deklaracje zawierają parametry typu, zostaną usunięte. JeśliK
nie jest równe zeru, wszyscy członkowie z inną liczbą parametrów typu zostaną usunięci. JeśliK
jest zero, metody o parametrach typu nie są usuwane, ponieważ proces wnioskowania typu (§11.6.3) może być w stanie wywnioskować argumenty typu.- Następnie niech F będzie jednostką kompilacji, która zawiera wyrażenie, w którym występuje wyszukiwanie składowych. Wszyscy członkowie, którzy są typami lokalnymi pliku i nie są deklarowani w F, są usuwani z zestawu.
- Dalej, jeśli zestaw dostępnych elementów członkowskich zawiera typy plików lokalnych, wszystkie elementy członkowskie, które nie są typami plików lokalnych, zostaną usunięte z zestawu.
Uwagi
Te reguły nie zezwalają na użycie typów plików lokalnych poza plikiem, w którym są deklarowane.
Te reguły zezwalają również typowi lokalnemu dla pliku zacieniować przestrzeń nazw lub typ nielokalny dla pliku.
// 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
}
}
Pamiętaj, że nie aktualizujemy sekcji dotyczącej zakresów w specyfikacji. Dzieje się tak, ponieważ zgodnie z treścią specyfikacji:
Zakres nazwy jest regionem tekstu programu, w którym można odwoływać się do jednostki zadeklarowanej przez nazwę bez kwalifikacji nazwy.
W rezultacie zakres wpływa tylko na wyszukiwanie nazw niekwalifikowanych. Nie jest to właściwa koncepcja, której należy użyć, ponieważ musimy również wpłynąć na wyszukiwanie kwalifikowanych nazw:
// 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
}
}
W związku z tym nie określamy cechy pod względem zakresu, w którym znajduje się typ, ale raczej jako dodatkowe "reguły filtrowania" w wyszukiwaniu składowych.
Atrybuty
Klasy lokalne plików mogą być typami atrybutów i mogą być używane jako atrybuty zarówno w typach plików lokalnych, jak i innych niż file-local, podobnie jak w przypadku, gdy typ atrybutu był typem nielokacyjnym. Nazwa metadanych typu atrybutu file-local nadal przechodzi przez tę samą strategię generowania nazw co inne typy plików lokalnych. Oznacza to, że wykrywanie obecności lokalnego typu pliku przy użyciu zakodowanej nazwy w postaci ciągu znaków może być niepraktyczne, ponieważ wymaga polegania na wewnętrznej strategii generowania nazw kompilatora, która może ulec zmianie w czasie. Jednak wykrywanie za pośrednictwem typeof(MyFileLocalAttribute)
działa.
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
}
}
Użycie w podpisach
Istnieje ogólna potrzeba uniemożliwienia wyświetlania typów plików lokalnych w parametrach, zwracach i ograniczeniach parametrów typu, w których typ pliku lokalnego może nie znajdować się w zakresie w momencie użycia elementu członkowskiego.
Należy pamiętać, że typy inne niż file-local mogą implementować interfejsy plików lokalnych, podobnie jak typy mogą implementować mniej dostępne interfejsy. W zależności od typów obecnych w elementach członkowskich interfejsu może to spowodować naruszenie reguł w poniższej sekcji.
Zezwalaj tylko na użycie podpisów w członkach typów lokalnych plików
Być może najprostszym sposobem, aby to zapewnić, jest wymuszenie, że typy lokalne dla pliku mogą pojawiać się tylko w sygnaturach lub jako typy bazowe innych typów lokalnych dla pliku.
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
}
Należy pamiętać, że ogranicza to użycie w jawnych implementacjach, mimo że takie użycie jest bezpieczne. Robimy to w celu uproszczenia reguł początkowej iteracji funkcji.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
Błąd kompilacji występuje, gdy używa się typu zdefiniowanego lokalnie w pliku w dyrektywie global using static
, to znaczy
global using static C; // error
file class C
{
public static void M() { }
}
Implementacja/nadpisania
Deklaracje typów plików lokalnych mogą implementować interfejsy, zastępować metody wirtualne itp., podobnie jak zwykłe deklaracje typów.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications