Локальные типы файлов
Заметка
Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.
Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия зафиксированы в соответствующих заметках по проектированию языка (LDM) .
Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .
Выпуск Чемпион: https://github.com/dotnet/csharplang/issues/5529
Сводка
Разрешить модификатор file
в объявлениях типов верхнего уровня. Тип существует только в файле, в котором он объявлен.
// 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.
Мотивация
Наша основная мотивация мотивируется генераторами кода. Генераторы источников работают путем добавления файлов в компиляцию пользователя.
- Эти файлы должны содержать сведения о реализации, скрытые от остальной части компиляции, но доступны для использования в файле, в котором они объявлены.
- Мы хотим снизить необходимость генераторам искать имена типов, которые не будут конфликтовать с объявлениями в пользовательском коде или коде других генераторов.
Подробный дизайн
- Мы добавим модификатор
file
в следующие наборы модификаторов: - Модификатор
file
можно использовать только в типе верхнего уровня.
Если тип имеет модификатор file
, он считается типом локального файла.
Доступность
Модификатор file
не классифицируется как модификатор специальных возможностей. Модификаторы доступности нельзя использовать в сочетании с file
на типе.
file
рассматривается как отдельная концепция, не связанная с доступностью. Так как локальные типы файлов не могут быть вложены, только доступность по умолчанию internal
может быть использована с file
типами.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Именование
Реализация гарантирует, что локальные типы файлов в разных файлах с одинаковым именем будут отличаться от среды выполнения. Доступность и имя типа в метаданных определяются реализацией. Цель заключается в том, чтобы компилятор принял все будущие функции ограничения доступа в среде выполнения, которые подходят для этой функции. Ожидается, что в начальной реализации будет использоваться доступность internal
, и будет использоваться нечитаемое сгенерированное имя, которое зависит от файла, в котором объявлен тип.
Поиск
Мы
- Затем, если
K
равно нулю, все вложенные типы, объявления которых включают параметры типа, удаляются. ЕслиK
не равно нулю, удаляются все члены с другим числом параметров типа. ЕслиK
равно нулю, методы с параметрами типа не удаляются, так как процесс вывода типов (§11.6.3) может иметь возможность выводить аргументы типа.- Затем давайте F быть единицей компиляции, содержащей выражение, в котором происходит поиск элементов. Все члены, которые являются локальными типами файлов и не объявляются в F удаляются из набора.
- Далее, если набор доступных элементов содержит локальные типы файлов, все члены, которые не являются локальными типами файлов, удаляются из набора.
Замечания
Эти правила запрещают использование локальных типов файлов за пределами файла, в котором они объявлены.
Эти правила также позволяют локальному типу файла затмевать пространство имен или тип, не являющийся локальным для файла.
// 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
}
}
Обратите внимание, что мы не обновляем раздел спецификации областей . Это связано с тем, как указано в спецификации:
Область имени — это область текста программы, в которой можно ссылаться на сущность, объявленную именем без квалификации имени.
В действительности область видимости влияет только на процедуру поиска неквалифицированных имен. Это не совсем подходящая для нас концепция, поскольку нам также нужно влиять на процесс поиска квалифицированных имен.
// 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
}
}
Поэтому мы не указываем характеристику с точки зрения области, в которой содержится тип, а скорее как дополнительные "правила фильтрации" в поиске членов.
Атрибуты
Локальные для файла классы могут быть типами атрибутов и могут использоваться как атрибуты как в типах, локальных для файла, так и в типах, не локальных для файла, так же, как если бы тип атрибута был не локальным для файла. Имя метаданных типа атрибута file-local по-прежнему формируется с использованием той же стратегии генерации имен, что и другие file-local типы. Это означает, что обнаружение наличия локального типа файла по жестко закодированному имени строки, скорее всего, будет непрактичным, так как оно требуется в зависимости от стратегии создания внутренних имен компилятора, которая может измениться с течением времени. Однако обнаружение с помощью 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
}
}
Использование в подписях
Существует общая необходимость запретить появление локальных типов файлов в параметрах членов, возвратах и ограничениях параметров типа, где локальный тип файла может быть недоступен в момент его использования.
Обратите внимание, что нефайловым локальным типам разрешено реализовывать интерфейсы, привязанные к файлам, аналогично тому, как типы могут реализовывать менее доступные интерфейсы. В зависимости от типов, присутствующих в членах интерфейса, это может привести к нарушению правил в следующем разделе.
Разрешить использование подписи только в членах локальных типов файлов
Возможно, самым простым способом обеспечения этого является принудительное применение локальных типов файлов только в сигнатурах или в качестве базовых типов других локальных типов файлов:
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
}
Обратите внимание, что это ограничивает использование в явных реализациях, даже если такие использования являются безопасными. Для упрощения правил начальной итерации функции мы делаем это.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
Это ошибка на этапе компиляции — использование локального типа файла в директиве global using static
, т. е.
global using static C; // error
file class C
{
public static void M() { }
}
Реализация/переопределения
Объявления типа file-local могут реализовывать интерфейсы, переопределять виртуальные методы и т. д., как и обычные объявления типов.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications