#c# #.net-3.5 #custom-attributes
#c# #.net-3.5 #пользовательские атрибуты
Вопрос:
Я разрабатываю пользовательский класс атрибутов.
public class MyAttr: Attribute
{
public ValueRange ValRange { get; set; }
}
Затем я пытаюсь присвоить этот атрибут свойству в соседнем классе:
public class Foo
{
[MyAttr(ValRange= new ValueRange())]
public string Prop { get; set; }
}
Однако компилятор жалуется на следующее:
‘ValRange’ не является допустимым аргументом именованного атрибута, поскольку он не является допустимым типом параметра атрибута
Я также попытался преобразовать класс ValueRange в struct
в надежде, что стать типом значения может решить проблему. Есть ли какой-либо способ обойти это?
Ответ №1:
Есть ли какой-либо способ обойти это?
Нет.
Для получения более подробной информации я отсылаю вас к разделу 17.1.3 спецификации C # 4, который я воспроизвожу здесь для вашего удобства:
Типы позиционных и именованных параметров для класса атрибутов ограничены типами параметров атрибута, которые являются:
- Один из следующих типов: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
- Объект типа.
- Тип System.Type.
- Тип перечисления, при условии, что он имеет общедоступный доступ, и типы, в которые он вложен (если таковые имеются), также имеют общедоступный доступ.
- Одномерные массивы вышеуказанных типов.
Аргумент конструктора или общедоступное поле, которое не имеет ни одного из этих типов, не может использоваться в качестве позиционного или именованного параметра в спецификации атрибута.
Помните, цель атрибута заключается в том, чтобы во время компиляции добавлять информацию к метаданным, связанным с объектом, на который вы поместили атрибут. Это означает, что вся информация, связанная с этим атрибутом, должна иметь четко определенный, однозначный способ сериализации ее в метаданные и из них. Ограничивая набор допустимых типов небольшим подмножеством всех возможных типов, мы гарантируем, что компилятор всегда может выдавать допустимые метаданные, понятные пользователю.
Комментарии:
1. Я помню, как впервые столкнулся с этим и подумал: «Ого. : (» Но потом я подумал о том, каким был бы мир, если бы я действительно мог это сделать, и понял, что в этом ограничении есть тонна смысла.
2. Я не нашел ничего, подтверждающего это, но я полагал, что это будет так. Спасибо за предоставленный ресурс. Но основано на документации … Я мог бы просто сохранить этот тип Object, а затем использовать его, когда мне нужно использовать его в коде в другом месте (что происходит очень редко). К сожалению, это будет связано с накладными расходами, но разве это было бы невозможно?
3. @Matthew: Нет. Значение, которое вы предоставляете для поля object, должно быть полностью известно во время компиляции либо как константа одного из заданных типов, null, выражение typeof, либо как одномерный массив, где все значения в нем аналогично «известны во время компиляции». Опять же, я повторяю ключевой момент: целью атрибута является добавление информации к метаданным во время компиляции . Компилятор должен иметь полное представление о добавляемой информации. Атрибуты — это не код, который выполняется при запуске программы; это дополнительные метаданные, которые вставляются в скомпилированную программу.
4. ах, я понимаю. Это имеет смысл после переосмысления. Я просто взял класс и превратил его также в атрибут, и этот подход решил проблему.
5. «Я просто взял класс и превратил его также в атрибут, и этот подход решил проблему». — Для некоторого определения «решаемого»?
Ответ №2:
Значения параметров атрибута должны быть разрешимы во время компиляции (т. е. константы).
Смотрите Attribute Parameter Types
в MSDN:
Значения, передаваемые атрибутам, должны быть известны компилятору во время компиляции.
Если вы можете создать ValueRange
константу, вы можете ее использовать.
Ответ №3:
Есть ли какой-либо способ обойти это?
ДА.
Вы можете заставить свой атрибут использовать Type
свойство, а затем использовать типы, которые реализуют определенный интерфейс, для которого код, обрабатывающий этот атрибут, должен был бы предполагать, и, таким образом, также создать неявное, но, надеюсь, документированное требование к своим клиентам:
public interface IValueRange {
int Start { get; }
int End { get; }
}
public class MyAttr : Attribute {
// The used type must implement IValueRange
public Type ValueRangeType { get; set; }
}
// ....
public class Foo {
class FooValueRange : IValueRange {
public int Start { get { return 10; } }
public int End { get { return 20; } }
}
[MyAttr(ValueRangeType = typeof(FooValueRange))]
public string Prop { get; set; }
}
Это мало чем отличается от многих классов в System.ComponentModel
пространстве имен, таких DesignerAttribute
как.
Ответ №4:
Параметры атрибута должны быть значениями следующих типов (цитирую статью):
- Простые типы (bool, byte, char, short, int, long, float и double)
- строка
- Система.Тип
- перечисления
- объект (Аргументом параметра атрибута типа object должно быть постоянное значение одного из вышеупомянутых типов.)
- Одномерные массивы любого из вышеупомянутых типов
Редактировать: изменено «константа времени компиляции» на «значение», поскольку типы и массивы не являются константами (спасибо комментатору, который указал на это (и впоследствии по какой-то причине удалил свой комментарий …))
Ответ №5:
Атрибуты могут получать в качестве параметров только константы времени компиляции (например, 3, «hello», typeof(MyClass), «путь к ресурсу, определяющему любые непостоянные данные, которые вам нужны»).
Последний пример (передача типа) Я привел, возможно, поможет вам разработать обходной путь (передать тип, реализующий интерфейс, с помощью нужного вам метода).