Почему C # запрещает универсальные типы атрибутов? - PullRequest
482 голосов
/ 16 ноября 2008

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

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

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

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

Ответы [ 7 ]

339 голосов
/ 16 ноября 2008

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

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

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

РЕДАКТИРОВАТЬ: Ответ от Эрика Липперта (перефразировано): нет особой причины, кроме как избежать сложности как в языке, так и в компиляторе для варианта использования, который не добавляет особой ценности.

80 голосов
/ 16 ноября 2008

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

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

18 голосов
/ 17 ноября 2008

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

[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
{
    ....
}
12 голосов
/ 07 ноября 2013

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

//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
{

}
8 голосов
/ 16 ноября 2008

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

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

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

1 голос
/ 03 января 2019

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

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

Несмотря на то, что это будет работать в принципе, в большинстве версии среды выполнения, чтобы она не работала правильно ( никогда не занимался).

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

Кандидат в основную версию C #, если мы можем сделать достаточное количество версий во время исполнения справляются с этим.

1 голос
/ 29 января 2016

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

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; }
...