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

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

  •  06-07-2019
  •  | 
  •  

Вопрос

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

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

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

Решение

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

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

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

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

так

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

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

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