Как получить член, к которому был применен мой пользовательский атрибут? - PullRequest
54 голосов
/ 30 января 2010

Я создаю пользовательский атрибут в C # и хочу делать разные вещи в зависимости от того, применяется ли атрибут к методу или свойству. Сначала я собирался сделать new StackTrace().GetFrame(1).GetMethod() в своем конструкторе атрибутов, чтобы увидеть, какой метод называется конструктором атрибутов, но теперь я не уверен, что это мне даст. Что если атрибут был применен к свойству? GetMethod() вернет MethodBase экземпляр для этого свойства? Есть ли другой способ получить член, к которому был применен атрибут в C #?

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property,
    AllowMultiple = true)]
public class MyCustomAttribute : Attribute

Обновление: хорошо, возможно, я задавал не тот вопрос. Как получить класс (или класс, содержащий член), к которому был применен мой пользовательский атрибут, из класса пользовательских атрибутов? Aaronaught предложил не подниматься по стеку, чтобы найти члена класса, к которому был применен мой атрибут, но как еще мне получить эту информацию из конструктора моего атрибута?

Ответы [ 4 ]

41 голосов
/ 30 января 2010

Поскольку в отношении работы стековых фреймов и методов возникает большая путаница, приведем простую демонстрацию:

static void Main(string[] args)
{
    MyClass c = new MyClass();
    c.Name = "MyTest";
    Console.ReadLine();
}

class MyClass
{
    private string name;

    void TestMethod()
    {
        StackTrace st = new StackTrace();
        StackFrame currentFrame = st.GetFrame(1);
        MethodBase method = currentFrame.GetMethod();
        Console.WriteLine(method.Name);
    }

    public string Name
    {
        get { return name; }
        set
        {
            TestMethod();
            name = value;
        }
    }
}

Вывод этой программы будет:

set_Name

Свойства в C # являются формой синтаксического сахара. Они компилируются в методы получения и установки в IL, и возможно, что некоторые языки .NET могут даже не распознавать их как свойства - разрешение свойств выполняется полностью по соглашению, в спецификации IL на самом деле нет никаких правил.

Теперь, допустим, на данный момент у вас была действительно веская причина, чтобы программа захотела проверить свой стек (и есть очень мало практических причин для этого). Почему в мире вы хотите, чтобы он вел себя по-разному для свойств и методов?

Весь смысл атрибутов в том, что они являются своего рода метаданными. Если вам нужно другое поведение, укажите его в атрибуте . Если атрибут может означать две разные вещи в зависимости от того, применяется ли он к методу или свойству - тогда у вас должно быть два атрибута . Установите для первого значения значение AttributeTargets.Method, а для второго значение AttributeTargets.Property. Простой.

Но, опять же, обход собственного стека для получения некоторых атрибутов из вызывающего метода в лучшем случае опасен. В некотором смысле, вы замораживаете дизайн своей программы, делая намного более сложным для кого-либо расширение или рефакторинг. Это не способ, которым атрибуты обычно используются. Более соответствующий пример будет выглядеть как атрибут проверки:

public class Customer
{
    [Required]
    public string Name { get; set; }
}

Тогда ваш код валидатора, который ничего не знает о фактической передаваемой сущности, может сделать это:

public void Validate(object o)
{
    Type t = o.GetType();
    foreach (var prop in
        t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
    {
        if (Attribute.IsDefined(prop, typeof(RequiredAttribute)))
        {
            object value = prop.GetValue(o, null);
            if (value == null)
                throw new RequiredFieldException(prop.Name);
        }
    }
}

Другими словами, вы изучаете атрибуты экземпляра, который был вам дан , но который вы не обязательно знаете о типе. Атрибуты XML, атрибуты Контракта данных, даже атрибуты Attribute - почти все атрибуты в .NET Framework используются таким образом, чтобы реализовать некоторые функциональные возможности, которые являются динамическими по отношению к типу экземпляра , но не по отношению к состояние программы или что находится в стеке. Маловероятно, что вы фактически управляете этим в точке, где вы создаете трассировку стека.

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

Если вам абсолютно необходимо (не говорите, что мы вас не предупреждали), тогда используйте два атрибута, один из которых может применяться к методам, а другой - к свойствам. Я думаю, вы обнаружите, что с ним гораздо проще работать, чем с одним супер-атрибутом.

40 голосов
/ 30 января 2010

Атрибуты предоставляют метаданные и ничего не знают о том, что (класс, член и т. Д.) Они украшают. С другой стороны, украшаемая вещь может запрашивать атрибуты, которыми она украшена.

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

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, 
    AllowMultiple = true)] 
public class MyCustomAttribute : Attribute
{
   Type type;

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}
4 голосов
/ 30 января 2010
Пользовательские атрибуты

активируются некоторым кодом, вызывающим метод GetCustomAttributes в ICustomAttributeProvider (объект отражения), который представляет местоположение, где применяется атрибут.Таким образом, в случае свойства, некоторый код получит PropertyInfo для свойства и затем вызовет GetCustomAttributes для этого.

Если вы хотите создать некоторую платформу проверки, вам нужно написать код, который проверяет типы &члены для пользовательских атрибутов.Например, у вас может быть интерфейс, который атрибуты реализуют для участия в вашей среде валидации.Может быть так просто:

public interface ICustomValidationAttribute
{
    void Attach(ICustomAttributeProvider foundOn);
}

Ваш код может искать этот интерфейс на (например) типе:

var validators = type.GetCustomAttributes(typeof(ICustomValidationAttribute), true);
foreach (ICustomValidationAttribute validator in validators)
{
     validator.Attach(type);
}

(предположительно, вы пройдете весь график отражения)и сделайте это для каждого ICustomAttributeProvider).В качестве примера аналогичного подхода в действии в .net FX вы можете посмотреть на «поведение» WCF (IServiceBehavior, IOperationBehavior и т. Д.).

Обновление: .net FX действительно имеет своего рода общее назначение, но в основном недокументированная среда перехвата в виде ContextBoundObject и ContextAttribute.Вы можете найти в Интернете некоторые примеры использования его для АОП.

4 голосов
/ 30 января 2010

GetMethod всегда вернет вам имя функции. Если это свойство, вы получите либо get_PropertyName, либо set_PropertyName.

Свойство - это в основном тип метода, поэтому при реализации свойства компилятор создает две отдельные функции в результирующем MSIL, методы get_ и a set_. Вот почему в трассировке стека вы получаете эти имена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...