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

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

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

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

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

4b9b3361

Ответ 1

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

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

Свойства в С# являются формой синтаксического сахара. Они сводятся к методам getter и setter в 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, атрибуты Data Contract и атрибуты атрибута - почти все атрибуты в .NET Framework используются таким образом, чтобы реализовать некоторые функциональные возможности, которые являются динамическими по отношению к типу экземпляра, но не относятся к состоянию программы или что происходит в стеке. Очень маловероятно, что вы фактически контролируете это в том месте, где вы создаете трассировку стека.

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

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

Ответ 2

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

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

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

   public MyCustomAttribute(Type type)
   {
      this.type = type;
   }
}

Ответ 3

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

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

Ответ 4

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

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

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

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

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. Вы можете искать в Интернете некоторые примеры использования его для АОП.