Почему C# запрещает общие типы атрибутов?

Это вызывает исключение времени компиляции:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

Я понимаю, что C# не поддерживает общие атрибуты. Однако после долгого поиска в Google я не могу найти причину.

Кто-нибудь знает, почему универсальные типы не могут быть производными от Attribute? Есть теории?

Вы могли бы сделать [Validates (typeof (string)] - я согласен, что дженерики были бы лучше ...

ConsultUtah 01.03.2010 22:43

Несмотря на то, что это очень позднее добавление к этому вопросу, грустно, что не только сами атрибуты, но и абстрактные классы атрибутов (которые, очевидно, в любом случае не могут быть созданы как атрибуты) не являются единственными, например: abstract class Base<T>: Attribute {}, который можно использовать для создания не -общие производные классы, подобные этому: class Concrete: Base<MyType> {}

Lucero 19.05.2010 22:46

Я жажду общих атрибутов и атрибутов, принимающих лямбды. Представьте себе такие вещи, как [DependsOnProperty<Foo>(f => f.Bar)] или [ForeignKey<Foo>(f => f.IdBar)] ...

Jacek Gorgoń 10.08.2011 23:39

Это было бы чрезвычайно полезно в ситуации, с которой я только что столкнулся; было бы неплохо создать LinkedValueAttribute, который принимает универсальный тип и применяет этот тип к указанному фактическому значению. Я мог бы использовать его для перечислений, чтобы указать значение «по умолчанию» другого перечисления, которое следует использовать, если выбрано это значение перечисления. Для разных типов можно указать несколько из этих атрибутов, и я могу получить нужное значение в зависимости от типа, который мне нужен. Я могу настроить его на использование типа и объекта, но строгая типизация будет огромным плюсом.

KeithS 22.08.2012 22:42

Если вы не против немного IL, это выглядит многообещающим.

Jarrod Dixon 09.10.2012 22:39

@JarrodDixon: Интересный подход. Для конкретного сценария, который я описываю, я действительно хотел бы создать экземпляр универсального атрибута в точке использования. Однако я буду помнить о подходе IL.

Bryan Watts 09.10.2012 23:51

Я согласен, точка использования - это то, как я хотел, чтобы эта функция работала - возможно, в будущей версии :)

Jarrod Dixon 10.10.2012 00:17

Какое совпадение. Я здесь для немного другого варианта использования, но он также связан с атрибутами проверки. Я хочу использовать ValidateIfAttribute<TValidationAttribute>.

Shimmy Weitzhandler 31.07.2017 03:12

Это отслеживается для включения в будущую версию C#: github.com/dotnet/csharplang/issues/124

Ian Kemp 07.11.2018 11:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
527
9
74 202
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Атрибут украшает класс во время компиляции, но универсальный класс не получает окончательную информацию о типе до времени выполнения. Поскольку атрибут может повлиять на компиляцию, он должен быть «завершен» во время компиляции.

См. Этот Статья MSDN для получения дополнительной информации.

В статье повторяется, что это невозможно, но без причины. Я концептуально понимаю ваш ответ. Знаете ли вы еще какую-либо официальную документацию по этому вопросу?

Bryan Watts 16.11.2008 22:21

В статье действительно рассматривается тот факт, что IL по-прежнему содержит общие заполнители, которые заменяются фактическим типом во время выполнения. Остальное сделал я сам ... :)

GalacticCowboy 16.11.2008 22:46

Как бы то ни было, VB применяет то же ограничение: «Классы, которые являются универсальными или содержатся в универсальном типе, не могут наследовать от класса атрибутов».

GalacticCowboy 16.11.2008 23:09

ECMA-334, раздел 14.16 говорит: «Постоянные выражения требуются в контекстах, перечисленных ниже, и это указывается в грамматике с помощью константного выражения. В этих контекстах возникает ошибка времени компиляции, если выражение не может быть полностью вычислено при компиляции. время." Атрибуты есть в списке.

GalacticCowboy 16.11.2008 23:44

Кажется, это противоречит другому ответу, в котором говорится, что IL это разрешит. (stackoverflow.com/a/294259/3195477)

StayOnTarget 09.05.2018 21:27

@DaveInCaz Это так. Но на данный момент обоим ответам уже 10 лет. IL это позволяет, а C# - нет, и причина в основном в том, что «мы не хотели».

GalacticCowboy 10.05.2018 22:53

Это очень хороший вопрос. По моему опыту работы с атрибутами, я думаю, что ограничение существует, потому что при размышлении об атрибуте оно создаст условие, в котором вам придется проверять все возможные перестановки типов: typeof(Validates<string>), typeof(Validates<SomeCustomType>) и т. д.

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

Возможно, класс проверки, который принимает в качестве параметра SomeCustomValidationDelegate или ISomeCustomValidator, будет лучшим подходом.

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

Bryan Watts 16.11.2008 22:24

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

bambams 14.01.2010 00:54

вы можете проверить определения общего типа (например, typeof (Validates <>)) ...

Melvyn 25.07.2013 00:05
Ответ принят как подходящий

Что ж, я не могу ответить, почему он недоступен, но я может подтверждаю, что это не проблема CLI. Спецификация CLI не упоминает об этом (насколько я понимаю), и если вы используете IL напрямую, вы можете создать общий атрибут. Часть спецификации C# 3, которая его запрещает - раздел 10.1.4 «Базовая спецификация класса» не дает никаких оснований.

Аннотированная спецификация ECMA C# 2 также не дает никакой полезной информации, хотя дает пример того, что не разрешено.

Моя копия аннотированной спецификации C# 3 должна прибыть завтра ... Я посмотрю, есть ли здесь дополнительная информация. В любом случае, это определенно языковое решение, а не время выполнения.

Обновлено: ответ Эрика Липперта (перефразировано): без особой причины, за исключением того, чтобы избежать сложности как в языке, так и в компиляторе для варианта использования, который не добавляет большой ценности.

Я рад слышать, что общие атрибуты являются законными IL. Я был бы очень признателен, если бы вы спросили команду! У меня была возможность спросить Андерса в PDC, но я совершенно забыл :-)

Bryan Watts 16.11.2008 22:40

Я спросил ... посмотрим, ответят ли они.

Jon Skeet 16.11.2008 23:04

«за исключением того, чтобы избежать сложности как в языке, так и в компиляторе» ... и это от людей, которые дают нам согласованность и контравариантность ...

flq 22.01.2009 00:41

Просто хотел задать тот же вопрос об атрибутах. К сожалению, общие атрибуты недоступны. Хуже того, для этого нет особой причины. : /

Arnis Lapsa 11.06.2009 15:58

"вариант использования, который не добавляет особой ценности"? Это субъективное мнение, оно может принести мне большую пользу!

Jon Kruger 21.09.2009 23:00

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

Chris Marisic 31.12.2009 21:10

Больше всего меня беспокоит отсутствие этой функции, так это невозможность делать такие вещи, как [PropertyReference (x => x.SomeProperty)]. Вместо этого вам нужны волшебные строки и typeof (), что, на мой взгляд, отстой.

Asbjørn Ulsberg 25.06.2011 16:38

@asbjornu: Да, хотя иметь оператор infoof было бы лучше, чем использовать лямбда-выражение ...

Jon Skeet 25.06.2011 17:10

Понятно избегать ненужной сложности языка и компилятора, пока вы не захотите использовать эту функцию, как я сейчас. :) На этом этапе я бы сказал, что в языке уже есть универсальные шаблоны и атрибуты, так почему бы просто не смешать их вместе - может быть, летнему стажеру нужно чем-то заняться в Microsoft.

John K 08.08.2011 19:33

@John: Я думаю, вы сильно недооцениваете стоимость разработки, определения, внедрения и тестирования новой языковой функции.

Jon Skeet 08.08.2011 20:08

@Jon, действительно, это было немного насмешливо. Пропустил включение стандартного смайлика в конце.

John K 08.08.2011 21:31

Я просто хочу добавить в защиту @ Timwi, что это не единственное место они обсуждаются, и 13 тысяч просмотров по этому вопросу подразумевают здоровый уровень интереса. Также: спасибо за авторитетный ответ, Джон.

Jordan Gray 29.01.2013 15:51

@Jon: Есть некоторые ситуации, когда язык запрещает конструкцию (например, использование System.Enum в качестве общего ограничения), которая при отсутствии запрещающих правил просто «работает» по умолчанию. Хотя дизайнеры вольны делать все, что захотят, философия инвестирования усилий только там, где это приносит пользу, предполагает, что дизайнеры не должны прилагать усилий, запрещая такие конструкции, если это не принесет какой-то выгоды. Каким будет эффект, если позволить универсальным типам атрибутов вести себя так, как они «естественны», если бы C# не запретил их?

supercat 09.03.2014 01:51

Оператор infoof был бы очень хорош! Я думаю, что это устраняет необходимость в общих атрибутах и ​​решает другие проблемы, подобные описанной в этом вопросе: stackoverflow.com/q/18119857/358761

ygormutti 05.08.2014 04:17

@ygormutti: C# 6 будет иметь nameof ... не такой мощный, как infoof, но значительно проще.

Jon Skeet 05.08.2014 10:40

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

N-ate 29.10.2014 20:07

@tacos: вы уже можете получить релиз-кандидат VS2015 с поддержкой C# 6.

Jon Skeet 21.06.2015 12:29

@JonSkeet Я не заглядывал вперед достаточно, я видел видео несколько недель назад, но я спрашивал об общих атрибутах. Это действительно снизило бы входной барьер для некоторых новых программистов на C#, которые, я думаю, привыкли к синтаксису, который используется для создания форм и помощников в шаблонах представлений. Я помню, всего несколько лет назад, я был так взволнован, когда понял, что могу создавать свои собственные атрибуты после прочтения вашей книги - но затем разочаровался, когда не смог сделать [Cascade(x => x.CountryCode)] - что-то с избранными списками, я думаю, я не могу вспомнить - но, чувак, это было бы круто.

tacos_tacos_tacos 21.06.2015 12:37

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

James M 02.11.2016 00:53

Его 2020 и C# 8.0 и общие атрибуты по-прежнему не разрешены; Для меня вариант использования прямо сейчас заключался бы в том, чтобы сделать "примерно таким же, как" [SomeAttribute<SomeType>], но с возможностью ограничить WHICH типы, которые могут туда идти, потому что при выполнении [SomeAttribute(typeof(SomeType))] программа обречена на сбой только во время выполнения, и это заставляет разработку программ сложнее и утомительнее. Я не могу найти другой способ получить ошибку времени компиляции вместо ошибки времени выполнения или "трудно-отлаживаемое снисходительное" игнорировать этот атрибут, если он недействителен ""

Felype 17.01.2020 17:09

@Felype он включен в дорожную карту C# 10.0, см. Конец обсуждения csharplang здесь

McGuireV10 30.09.2020 14:21

Я не знаю, почему это запрещено, но это одно из возможных решений.

[AttributeUsage(AttributeTargets.Class)]
public class ClassDescriptionAttribute : Attribute
{
    public ClassDescriptionAttribute(Type KeyDataType)
    {
        _KeyDataType = KeyDataType;
    }

    public Type KeyDataType
    {
        get { return _KeyDataType; }
    }
    private Type _KeyDataType;
}


[ClassDescriptionAttribute(typeof(string))]
class Program
{
    ....
}

К сожалению, при использовании атрибута вы теряете типизацию во время компиляции. Представьте, что атрибут создает нечто общего типа. Вы можете обойти это, но это было бы хорошо; это одна из тех интуитивных вещей, которые вы удивлены, что не можете сделать, например, дисперсия (в настоящее время).

Bryan Watts 17.11.2008 03:20

К сожалению, пытаясь этого не делать, я нашел этот ТАК вопрос. Думаю, мне просто придется заниматься typeof. Сейчас это действительно кажется грязным ключевым словом после того, как дженерики существуют так долго.

Chris Marisic 31.12.2009 21:12

Это не отвечает на вопрос.

O. R. Mapper 04.01.2021 22:30

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

//an interface which means it can't have its own implementation. 
//You might need to use extension methods on this interface for that.
public interface ValidatesAttribute<T>
{
    T Value { get; } //or whatever that is
    bool IsValid { get; } //etc
}

public class ValidatesStringAttribute : Attribute, ValidatesAttribute<string>
{
    //...
}
public class ValidatesIntAttribute : Attribute, ValidatesAttribute<int>
{
    //...
}

[ValidatesString]
public static class StringValidation
{

}
[ValidatesInt]
public static class IntValidation
{

}

Мой обходной путь выглядит примерно так:

public class DistinctType1IdValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type1> validator;

    public DistinctIdValidation()
    {
        validator = new DistinctValidator<Type1>(x=>x.Id);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

public class DistinctType2NameValidation : ValidationAttribute
{
    private readonly DistinctValidator<Type2> validator;

    public DistinctType2NameValidation()
    {
        validator = new DistinctValidator<Type2>(x=>x.Name);
    }

    public override bool IsValid(object value)
    {
        return validator.IsValid(value);
    }
}

...
[DataMember, DistinctType1IdValidation ]
public Type1[] Items { get; set; }

[DataMember, DistinctType2NameValidation ]
public Type2[] Items { get; set; }

В настоящее время это не функция языка C#, однако есть много дискуссий об официальном репозитории языка C#.

От некоторые заметки о встрече:

Even though this would work in principle, there are bugs in most versions of the runtime so that it wouldn't work correctly (it was never exercised).

We need a mechanism to understand which target runtime it works on. We need that for many things, and are currently looking at that. Until then, we can't take it.

Candidate for a major C# version, if we can make a sufficient number of runtime versions deal with it.

Другие вопросы по теме