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

210

У меня есть тип, tи я хотел бы получить список открытых свойств, которые имеют атрибут MyAttribute. Атрибут помечен AllowMultiple = false, как это:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]

В настоящее время у меня есть это, но я думаю, что есть лучший способ:

foreach (PropertyInfo prop in t.GetProperties())
{
    object[] attributes = prop.GetCustomAttributes(typeof(MyAttribute), true);
    if (attributes.Length == 1)
    {
         //Property with my custom attribute
    }
}

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

wsanville
источник
Нет. Вам нужно PropertyInfo, прежде чем вы сможете узнать, имеет ли свойство атрибут.
Ганс Пассант

Ответы:

391
var props = t.GetProperties().Where(
                prop => Attribute.IsDefined(prop, typeof(MyAttribute)));

Это позволяет избежать материализации любых экземпляров атрибута (т.е. это дешевле, чем GetCustomAttribute[s]().

Марк Гравелл
источник
1
Хорошее предложение. Однако мне понадобится экземпляр атрибута, но мне это нравится.
wsanville
1
Я просто искал способ проверить наличие атрибута без побочного эффекта, вызываемого свойством get. Спасибо Марк, это работает!
Орджан Ямте
1
@ ÖrjanJämte свойство getне вызывается даже при использовании GetCustomAttributes; однако атрибут создается , что не является бесплатным. Если вам не нужно проверять конкретные значения атрибута, IsDefinedэто дешевле. И в 4.5 есть способы проверить данные экземпляров без фактического создания каких-либо экземпляров атрибутов (хотя это предназначено только для очень специфических сценариев)
Марк Гравелл
2
@bjhuffine msdn.microsoft.com/en-us/library/…
Марк
2
для ядра dotnet: var props = t.GetProperties (). Где (e => e.IsDefined (typeof (MyAttribute)));
Rtype
45

Решение, которое я использую чаще всего, основано на ответе Томаса Петричека. Обычно я хочу сделать что - то с как атрибута и свойства.

var props = from p in this.GetType().GetProperties()
            let attr = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attr.Length == 1
            select new { Property = p, Attribute = attr.First() as MyAttribute};
wsanville
источник
+1 - «Я обычно хочу что-то сделать с атрибутом и свойством» - вот что я искал - большое спасибо за публикацию вашего ответа!
Явар Муртаза
34

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

var props = from p in t.GetProperties()
            let attrs = p.GetCustomAttributes(typeof(MyAttribute), true)
            where attrs.Length != 0 select p;

// Do something with the properties in 'props'

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

Томас Петричек
источник
13

Там всегда LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
P папа
источник
6

Если вы регулярно имеете дело с Атрибутами в Reflection, очень и очень практично определить некоторые методы расширения. Вы увидите это во многих проектах. Вот этот, который я часто имею:

public static bool HasAttribute<T>(this ICustomAttributeProvider provider) where T : Attribute
{
  var atts = provider.GetCustomAttributes(typeof(T), true);
  return atts.Length > 0;
}

который вы можете использовать как typeof(Foo).HasAttribute<BarAttribute>();

Другие проекты (например, StructureMap) имеют полноценные классы ReflectionHelper, которые используют деревья выражений для точного синтаксиса идентификации, например PropertyInfos. Использование тогда выглядит так:

ReflectionHelper.GetProperty<Foo>(x => x.MyProperty).HasAttribute<BarAttribute>()
FLQ
источник
2

В дополнение к предыдущим ответам: лучше использовать метод, Any()а не проверять длину коллекции:

propertiesWithMyAttribute = type.GetProperties()
  .Where(x => x.GetCustomAttributes(typeof(MyAttribute), true).Any());

Пример в dotnetfiddle: https://dotnetfiddle.net/96mKep

feeeper
источник
@ cogumel0 Прежде всего, конечно .Any(), не проверяет длину. Но мой ответ был не о найденных свойствах с одним атрибутом. Во-вторых, я не уверен, что вы правильно прочитали код - .Anyметод вызывается по результату GetCustomAttrubutesметода. Таким образом, типом propertiesWithMyAttributeбудет коллекция свойств. Посмотрите пример на dotnetfiddle (я добавляю ссылку на ответ).
Feeeper
1
Вы можете заменить .Where на .Any, поскольку .Any также разрешает лямбды.
PRMan