Ограничить пользовательский атрибут, чтобы его можно было применять только к конкретным типам в C #? - PullRequest
11 голосов
/ 20 июня 2009

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

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

Ответы [ 2 ]

15 голосов
/ 20 июня 2009

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

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

(оригинальный ответ)

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

Атрибуты могут быть ограничены (например, «класс», «структура», «метод», «поле» и т. Д.), Но ничего более гранулированного.

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

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

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

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

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

abstract class MyBase {
    protected abstract string GetSomeMagicValue {get;}
}
class MyActualType : MyBase {
    protected override string GetSomeMagicValue {get {return "Foo";}}
}
0 голосов
/ 20 июня 2009

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

так

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 закрытым, чтобы производные классы не могли его видеть и изменять, но могли сделать его защищенным и разрешить доступ к производным классам, если вы хотите.

...