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


Универсальные атрибуты

Заметка

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

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

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

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

Сводка

Когда универсальные шаблоны были введены в C# 2.0, классы атрибутов не были разрешены для участия. Мы можем сделать язык более компонуемым, удаляя это ограничение (скорее, ослабляя). Среда выполнения .NET Core добавила поддержку универсальных атрибутов. Теперь в компиляторе не хватает поддержки универсальных атрибутов.

Мотивация

В настоящее время авторы атрибутов могут принимать System.Type в качестве параметра, а пользователи могут передавать typeof выражение, чтобы предоставить атрибуту необходимые ему типы. Однако за пределами анализаторов нет способа ограничить автору атрибута, какие типы могут быть переданы атрибуту через typeof. Если атрибуты могут быть универсальными, авторы атрибутов могут использовать существующую систему ограничений параметров типа для выражения требований к типам, которые они принимают в качестве входных данных.

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

Следующий раздел изменен: §15.2.4.2

Прямой базовый класс типа класса не должен быть ни одного из следующих типов: System.Array, System.Delegate, System.MulticastDelegate, System.Enum или System.ValueType. Более того, объявление универсального класса не может использовать System.Attribute в качестве прямого или косвенного базового класса.

Важно отметить, что следующий раздел спецификации не влияет на при ссылке на точку использования атрибута, т. е. в списке атрибутов: параметры типа — §8.5.

Параметр типа нельзя использовать в любом месте атрибута.

Это означает, что при использовании универсального атрибута его построение должно быть полностью закрытым, т. е. не содержит параметров типа, что означает, что следующее по-прежнему запрещено:

using System;
using System.Collections.Generic;

public class Attr<T1> : Attribute { }

public class Program<T2>
{
    [Attr<T2>] // error
    [Attr<List<T2>>] // error
    void M() { }
}

Если универсальный атрибут используется в списке атрибутов, аргументы типа имеют те же ограничения, что и у typeof аргумента. Например, [Attr<dynamic>] является ошибкой. Это связано с тем, что типы, зависящие от атрибутов, такие как dynamic, List<string?>, nintи т. д., не могут быть полностью представлены в окончательном IL для аргумента типа атрибута, так как не существует символа для подключения DynamicAttribute или другого хорошо известного атрибута.

Недостатки

Удаление ограничения, обоснование последствий и добавление соответствующих тестов — это работа.

Альтернативы

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

Неразрешенные вопросы

  • [x] Что означает AllowMultiple = false для универсального атрибута? Если у нас есть [Attr<string>] и [Attr<object>], которые используются в символе, означает ли это, что используется несколько экземпляров атрибута?
    • В настоящее время мы склонны принять более строгий маршрут здесь и рассмотреть исходное определение класса атрибута при определении того, было ли применено несколько из них. Другими словами, [Attr<string>] и [Attr<object>] применяются вместе, несовместимы с AllowMultiple = false.

Встречи по проектированию

  • https://github.com/dotnet/csharplang/blob/main/meetings/2017/LDM-2017-02-21.md#generic-attributes
    • В то время существовала озабоченность по поводу того, что нам придется ограничить доступ к функции на том, поддерживает ли среда выполнения её. (Однако теперь мы поддерживаем только C# 10 в .NET 6. Было бы хорошо, чтобы реализация знала, какая минимальная целевая платформа поддерживает эту функцию, но кажется менее важной сегодня.)