Поделиться через


Расширение частичных методов

Заметка

Эта статья является спецификацией компонентов. Спецификация служит проектным документом для функции. Это включает предлагаемые изменения спецификации, а также информацию, необходимую во время проектирования и разработки функции. Эти статьи публикуются до тех пор, пока предложенные изменения спецификации не будут завершены и включены в текущую спецификацию ECMA.

Может возникнуть некоторое несоответствие между спецификацией компонентов и завершенной реализацией. Эти различия фиксируются в соответствующих собраниях по проектированию языка (LDM).

Дополнительные сведения о процессе внедрения спецификаций функций в стандарт языка C# см. в статье о спецификациях .

Проблема чемпиона: https://github.com/dotnet/csharplang/issues/3301

Сводка

Это предложение призвано удалить все ограничения вокруг подписей методов partial в C#. Цель заключается в том, чтобы расширить набор сценариев, в которых эти методы могут работать с генераторами исходного кода, а также представлять собой более общий формат объявления для методов C#.

См. также спецификацию исходных частичных методов (§15.6.9).

Мотивация

C# имеет ограниченную поддержку для разработчиков, разделяющих методы на объявления и определения / реализации.

partial class C
{
    // The declaration of C.M
    partial void M(string message);
}

partial class C
{
    // The definition of C.M
    partial void M(string message) => Console.WriteLine(message);
}

Один из поведений методов partial заключается в том, что если определение отсутствует, тогда язык просто удаляет все вызовы метода partial. По сути, он ведет себя как вызов метода [Conditional], где условие было оценено как false.

partial class D
{
    partial void M(string message);

    void Example()
    {
        M(GetIt()); // Call to M and GetIt erased at compile time
    }

    string GetIt() => "Hello World";
}

Исходная мотивация этой функции была источником создания в виде созданного конструктором кода. Пользователи постоянно редактировали созданный код, так как они хотели подключить некоторые аспекты созданного кода. Особенно части процесса запуска Windows Forms после инициализации компонентов.

Изменение сгенерированного кода было подвержено ошибкам, так как любое действие, которое вызывало повторное создание кода, приводило к удалению внесенных пользователем изменений. Функция метода partial облегчила эту напряженность, так как она позволила дизайнерам публиковать хуки в виде методов partial.

Конструкторы могут выдавать такие перехватчики, как partial void OnComponentInit(), и разработчики могут определять объявления для них или не определять их. В любом случае, созданный код будет компилироваться, и разработчики, заинтересованные в процессе, могут подключаться при необходимости.

Это означает, что частичные методы имеют несколько ограничений:

  1. Должен иметь тип возвращаемого значения void.
  2. Нельзя иметь параметры out.
  3. Невозможно иметь функции доступности (неявно private).

Эти ограничения существуют, так как язык должен иметь возможность выдавать код при удалении сайта вызова. Учитывая, что их можно удалить, private является единственной возможной доступностью, поскольку элемент не может быть представлен в метаданных сборки. Эти ограничения также служат для ограничения набора сценариев, в которых можно применять partial методы.

Это предложение заключается в удалении всех существующих ограничений вокруг методов partial. По сути, позволить им иметь параметры out, непустые типы возвращаемых значений или любой уровень доступа. Таким декларациям partial будут подлежать дополнительные требования о том, чтобы определение существовало. Это означает, что язык не должен учитывать влияние стирания мест вызова.

Это расширит набор сценариев генератора, в которых partial методы могут участвовать и, следовательно, лучше интегрироваться с нашей функцией генераторов источников. Например, можно определить regex с помощью следующего шаблона:

[RegexGenerated("(dog|cat|fish)")]
partial bool IsPetMatch(string input);

Это дает разработчикам простой декларативный способ подключаться к генераторам, а также предоставляет генераторам легкий набор деклараций для изучения исходного кода и управления генерируемыми данными.

Сравните это с трудностью, которую генератор испытал бы при подключении следующего фрагмента кода.

var regex = new RegularExpression("(dog|cat|fish)");
if (regex.IsMatch(someInput))
{

}

Учитывая, что компилятор не позволяет генераторам изменять код, связывание с этим паттерном будет практически невозможно для генераторов. Им потребуется прибегнуть к рефлексии в реализации IsMatch или попросить пользователей изменить свои места вызова на новый метод, а также рефакторинг регулярного выражения, чтобы передать строковый литерал в качестве аргумента. Это довольно грязно.

Подробный дизайн

Язык изменится, чтобы разрешить аннотировать методы partial явным модификатором доступности. Это означает, что они могут быть помечены как private, publicи т. д.

Если метод partial имеет явный модификатор доступности, язык потребует, чтобы объявление имело соответствующее определение, даже когда доступность private.

partial class C
{
    // Okay because no definition is required here
    partial void M1();

    // Okay because M2 has a definition
    private partial void M2();

    // Error: partial method M3 must have a definition
    private partial void M3();
}

partial class C
{
    private partial void M2() { }
}

Далее язык удалит все ограничения на то, что может присутствовать в методе partial, который имеет явно заданную доступность. Такие объявления могут содержать непустые типы возвращаемых значений, out параметры, модификатор extern и т. д. Эти подписи будут обладать полной выразительностью языка C#.

partial class D
{
    // Okay
    internal partial bool TryParse(string s, out int i); 
}

partial class D
{
    internal partial bool TryParse(string s, out int i) { ... }
}

Это даёт возможность методам partial участвовать в реализации overrides и interface.

interface IStudent
{
    string GetName();
}

partial class C : IStudent
{
    public virtual partial string GetName(); 
}

partial class C
{
    public virtual partial string GetName() => "Jarde";
}

Компилятор изменит текст ошибки, которую он выдает, когда метод partial содержит недопустимый элемент, чтобы фактически сказать:

Не удается использовать ref в методе partial, который не имеет явного модификатора доступа.

Это поможет разработчикам в правильном направлении при использовании этой функции.

Ограничения:

  • partial объявления с явно указанной доступностью должны иметь определение
  • partial объявления и сигнатуры определения должны совпадать на всех модификаторах методов и параметров. Единственными аспектами, которые могут отличаться, являются имена параметров и списки атрибутов (это не новое, а существующее требование методов partial).

Вопросы

частичный для всех членов

Учитывая, что мы расширяем partial, чтобы лучше поддерживать исходные генераторы, следует ли также расширить его возможности для работы со всеми членами класса? Например, если бы у нас была возможность объявлять partial конструкторы, операторы и т. д.

Решение Идея обоснованна, но на этом этапе графика C# 9 мы стараемся избежать ненужного наращивания функций. Хотите решить немедленную проблему расширения функции для работы с современными генераторами источников.

Расширение partial для поддержки других членов рассматривается для релиза C# 10. Кажется вероятным, что мы рассмотрим это расширение. Это остается активным предложением, но оно еще не реализовано.

Используйте абстрактное вместо частичного

Суть этого предложения заключается в обеспечении того, чтобы объявление имело соответствующее определение или реализацию. Следует ли нам использовать abstract, поскольку это уже ключевое слово языка, которое заставляет разработчика думать о наличии реализации?

резолюции Было здоровое обсуждение этого, но в конечном итоге было принято решение против. Да, требования знакомы, но концепции значительно отличаются. Может легко привести разработчика к тому, чтобы поверить в то, что они создают виртуальные слоты, хотя они этого не делали.