Почему C# запрещает универсальные типы атрибутов?
это вызывает исключение времени компиляции:
public sealed class ValidatesAttribute<T> : Attribute
{
}
[Validates<string>]
public static class StringValidation
{
}
Я понимаю, что C# не поддерживает общие атрибуты. Однако, после долгих поисков в Интернете, я не могу найти причину.
кто-нибудь знает, почему универсальные типы не могут вытекать из Attribute
? Есть какие-нибудь теории?
6 ответов:
Ну, я не могу ответить, почему это не доступно, но я can подтвердите, что это не проблема CLI. Спецификация CLI не упоминает об этом (насколько я могу видеть), и если вы используете IL напрямую, вы можете создать универсальный атрибут. Часть спецификации C# 3, которая запрещает ее-раздел 10.1.4 "спецификация базового класса", не дает никакого обоснования.
аннотированная спецификация ECMA C# 2 также не дает никакой полезной информации, хотя она дает пример того, что не является допустимый.
моя копия аннотированной спецификации C# 3 должна прибыть завтра... Я посмотрю, даст ли это дополнительную информацию. В любом случае, это определенно языковое решение, а не во время выполнения.
EDIT: ответ от Эрика Липперта (перефразированный): нет особой причины, за исключением того, чтобы избежать сложности как в языке, так и в компиляторе для случая использования, который не добавляет большого значения.
атрибут украшает класс во время компиляции, но универсальный класс не получает окончательную информацию о типе до времени выполнения. Поскольку атрибут может влиять на компиляцию, он должен быть "завершен" во время компиляции.
посмотреть этот статья 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
как a параметр был бы лучшим подходом.
мое решение-это что-то вроде этого:
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; }