Подтвердить что ты не робот

Ограничить пользовательский атрибут, чтобы он мог применяться только к конкретным типам в С#?

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

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

4b9b3361

Ответ 1

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

abstract class MyBase {
    [AttributeUsage(AttributeTargets.Property)]
    protected sealed class SpecialAttribute : Attribute {}
}

class ShouldBeValid : MyBase {
    [Special]
    public int Foo { get; set; }
}
class ShouldBeInvalid {
    [Special] // type or namespace not found
    [MyBase.Special] // inaccessible due to protection level
    public int Bar{ get; set; }
}

(исходный ответ)

Вы не можете сделать это - по крайней мере, не во время компиляции.

Атрибуты могут быть ограничены (для exmaple) до "class", "struct", "method", "field" и т.д., но не более гранулированы.

Конечно, поскольку атрибуты ничего не делают сами по себе (игнорируя PostSharp), они будут инертными по другим типам (и инертным по вашим типам, если вы не добавите некоторый код для просмотра атрибутов во время выполнения).

Вы можете написать правило FxCop, но это кажется излишним.

Интересно, однако, является ли атрибут лучшим выбором:

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

Если они должны применить свой собственный атрибут, возможно, лучший способ выбрать abstract (или, может быть, просто virtual)?

abstract class MyBase {
    protected abstract string GetSomeMagicValue {get;}
}
class MyActualType : MyBase {
    protected override string GetSomeMagicValue {get {return "Foo";}}
}

Ответ 2

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

так

class MyAttribute
{
     // put whatever custom data you want in here
}

interface IAttributeService<T> where T : MyBaseClass
{
     MyAttribute Value{get;}
}

то ваш базовый класс сделает это

class MyBaseClass : IAttributeService<MyBaseClass>
{
     private MyAttribute m_attrib;

     IAttributeService<MyClass>.Value
     {
         get {return m_attrib;}
     }
}

Таким образом, компилятор будет принудительно применять ограничение во время компиляции, чтобы только классы полученный от MyBaseClass, может реализовать интерфейс.
Я сделал его достаточно расширяемым, указав класс MyAttribute, но вы, конечно, можете просто вернуть строку или bool, если вам нужно что-то более простое

Я также сделал элемент MyAttribute частным, чтобы производные классы не могли его увидеть и изменить, но могли бы сделать его защищенным и разрешить доступ к производным классам, если вы захотите.