Wie erhalte ich eine Liste von Eigenschaften mit einem bestimmten Attribut?

210

Ich habe einen Typ, t und möchte eine Liste der öffentlichen Eigenschaften erhalten, die das Attribut haben MyAttribute. Das Attribut ist AllowMultiple = falsewie folgt gekennzeichnet :

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

Momentan habe ich Folgendes, aber ich denke, es gibt einen besseren Weg:

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

Wie kann ich das verbessern? Ich entschuldige mich, wenn dies ein Duplikat ist, gibt es da draußen eine Menge Reflexionsfäden ... scheint ein ziemlich heißes Thema zu sein.

wsanville
quelle
Nee. Sie benötigen eine PropertyInfo, bevor Sie herausfinden können, ob die Eigenschaft ein Attribut hat.
Hans Passant

Antworten:

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

Dadurch wird vermieden, dass Attributinstanzen materialisiert werden müssen (dh es ist billiger als GetCustomAttribute[s]().

Marc Gravell
quelle
1
Guter Vorschlag. Ich werde jedoch die Attributinstanz benötigen, aber es gefällt mir.
Wsanville
1
Ich habe nur nach einer Möglichkeit gesucht, die Existenz eines Attributs ohne den Nebeneffekt zu überprüfen, den die Eigenschaft aufruft. Danke Marc, es funktioniert!
Örjan Jämte
1
@ ÖrjanJämte die Eigenschaft getwird auch bei Verwendung nicht aufgerufen GetCustomAttributes; Das Attribut wird jedoch instanziiert , was nicht frei ist. Wenn Sie bestimmte Werte des Attributs nicht überprüfen müssen, IsDefinedist dies billiger. Und in 4.5 gibt es Möglichkeiten, die Instanziierungsdaten zu überprüfen, ohne tatsächlich Attributinstanzen zu erstellen (obwohl dies nur für sehr spezifische Szenarien vorgesehen ist)
Marc Gravell
2
@bjhuffine msdn.microsoft.com/en-us/library/…
Marc Gravell
2
für Dotnet-Kern: var props = t.GetProperties (). Where (e => e.IsDefined (typeof (MyAttribute)));
Rtype
45

Die Lösung, die ich am häufigsten verwende, basiert auf der Antwort von Tomas Petricek. Normalerweise möchte ich sowohl mit dem Attribut als auch mit der Eigenschaft etwas tun .

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
quelle
+1 - "Ich möchte normalerweise sowohl mit dem Attribut als auch mit der Eigenschaft etwas tun" ist das, wonach ich gesucht habe - vielen Dank für die Veröffentlichung Ihrer Antwort!
Yawar Murtaza
34

Soweit ich weiß, gibt es keinen besseren Weg, um mit der Reflection-Bibliothek intelligenter zu arbeiten. Sie können jedoch LINQ verwenden, um den Code ein bisschen schöner zu machen:

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'

Ich glaube, dies hilft Ihnen, den Code besser lesbar zu strukturieren.

Tomas Petricek
quelle
13

Es gibt immer LINQ:

t.GetProperties().Where(
    p=>p.GetCustomAttributes(typeof(MyAttribute), true).Length != 0)
P Papa
quelle
6

Wenn Sie sich regelmäßig mit Attributen in Reflection beschäftigen, ist es sehr, sehr praktisch, einige Erweiterungsmethoden zu definieren. Sie werden das in vielen Projekten sehen. Dieser hier ist einer, den ich oft habe:

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

die du gerne benutzen kannst typeof(Foo).HasAttribute<BarAttribute>();

Andere Projekte (z. B. StructureMap) verfügen über vollwertige ReflectionHelper-Klassen, die Ausdrucksbäume verwenden, um eine feine Syntax für die Identität zu erhalten, z. B. PropertyInfos. Die Verwendung sieht dann so aus:

ReflectionHelper.GetProperty<Foo>(x => x.MyProperty).HasAttribute<BarAttribute>()
flq
quelle
2

Zusätzlich zu den vorherigen Antworten: Es ist besser, die Methode zu verwenden, Any()anstatt die Länge der Sammlung zu überprüfen:

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

Das Beispiel bei dotnetfiddle: https://dotnetfiddle.net/96mKep

Feeeper
quelle
@ cogumel0 Zunächst einmal wird .Any()die Länge sicher nicht überprüft. Bei meiner Antwort ging es jedoch nicht um gefundene Eigenschaften mit genau einem Attribut. Zweitens bin ich mir nicht sicher, ob Sie den Code richtig gelesen haben - .AnyMethode, die für das Ergebnis der Methode aufgerufen GetCustomAttrubuteswird. Der Typ des propertiesWithMyAttributewird also die Sammlung der Eigenschaften sein. Schauen Sie sich das Beispiel bei dotnetfiddle an (ich füge den Link zur Antwort hinzu).
Feeeper
1
Sie können .Where durch .Any ersetzen, da .Any auch Lambdas zulässt.
PRMan