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

StackOverflow https://stackoverflow.com/questions/294216

  •  08-07-2019
  •  | 
  •  

Вопрос

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

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

[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
{
    ....
}

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

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

}

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

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

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

Мой обходной путь примерно такой:

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 # Сделки рЕПО .

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

  

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

     

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

     

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top