Wie alle Klassen mit benutzerdefinierter Klasse Attribute aufzählen?
-
03-07-2019 - |
Frage
Frage basierend auf MSDN Beispiel .
Lassen Sie uns sagen, dass wir einige C # -Klassen mit HelpAttribute im Standalone-Desktop-Anwendung haben. Ist es möglich, alle Klassen mit einem solchen Attribut aufzuzählen? Ist es sinnvoll, um Klassen auf diese Weise zu erkennen? Benutzerdefinierte Attribut würde verwendet werden, um mögliche Menüoptionen zur Liste, Element auswählen bringt Instanz dieser Klasse zu screenen. Anzahl der Klassen / Einzelteile werden langsam wachsen, aber auf diese Weise vermeiden wir können sie alle an anderer Stelle aufzuzählen, denke ich.
Lösung
Ja, absolut. Mit Reflection:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
Andere Tipps
Nun, würden Sie haben durch alle Klassen in allen Baugruppen aufzuzählen, die in der aktuellen Anwendungsdomäne geladen werden. Um das zu tun, würde rufen Sie die GetAssemblies
Methode auf die AppDomain
Instanz für die aktuelle Anwendungsdomäne.
Von dort würde rufen Sie GetExportedTypes
oder GetTypes
(wenn Sie nur öffentliche Typen wollen) a> auf jedem Assembly
die Arten zu erhalten, die in der Baugruppe enthalten ist .
Dann würde rufen Sie die GetCustomAttributes
Methode auf jedes Type
Beispiel, vorbei an den Typ des Attributs, das Sie finden möchten .
Sie können LINQ verwenden, um dies zu vereinfachen für Sie:
var typesWithMyAttribute =
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
where attributes != null && attributes.Length > 0
select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };
Die obige Abfrage werden Ihnen jede Art mit Ihrem Attribute erhält auf mich angewandt, zusammen mit der Instanz des Attributs (n) zugeordnet es.
Beachten Sie, wenn Sie eine große Anzahl von Baugruppen in Ihre Anwendungsdomäne geladen haben, dass der Betrieb teuer sein könnte. Sie können Parallel LINQ verwenden, um die Zeit der Operation zu reduzieren, wie so :
var typesWithMyAttribute =
// Note the AsParallel here, this will parallelize everything after.
from a in AppDomain.CurrentDomain.GetAssemblies().AsParallel()
from t in a.GetTypes()
let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
where attributes != null && attributes.Length > 0
select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };
Das Filtern sie auf einem bestimmten Assembly
ist einfach:
Assembly assembly = ...;
var typesWithMyAttribute =
from t in assembly.GetTypes()
let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
where attributes != null && attributes.Length > 0
select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };
Und wenn die Anordnung eine große Anzahl von Arten in sich hat, dann können Sie Parallel LINQ wieder:
Assembly assembly = ...;
var typesWithMyAttribute =
// Partition on the type list initially.
from t in assembly.GetTypes().AsParallel()
let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
where attributes != null && attributes.Length > 0
select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };
Andere Antworten Referenz GetCustomAttributes . Addiert man diese ein als Beispiel für die Verwendung IsDefined
Assembly assembly = ...
var typesWithHelpAttribute =
from type in assembly.GetTypes()
where type.IsDefined(typeof(HelpAttribute), false)
select type;
Wie bereits erwähnt, Reflexion ist der Weg zu gehen. Wenn Sie diese gehen häufig zu nennen, ich sehr empfehlen die Ergebnisse zwischenspeichern, wie Reflexion, vor allem durch jede Klasse aufzählt, kann sehr langsam sein.
Dies ist ein Ausschnitt aus meinem Code, der in allen geladenen Baugruppen alle Arten läuft durch:
// this is making the assumption that all assemblies we need are already loaded.
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type type in assembly.GetTypes())
{
var attribs = type.GetCustomAttributes(typeof(MyCustomAttribute), false);
if (attribs != null && attribs.Length > 0)
{
// add to a cache.
}
}
}
Dies ist eine Leistungssteigerung auf der akzeptierte Lösung. obwohl alle Klassen laufen kann langsam sein, weil es so viele sind. Manchmal kann man eine ganze Baugruppe herauszufiltern, ohne auf jeder seiner Art zu suchen.
Zum Beispiel, wenn Sie nach einem Attribut suchen, dass Sie selbst erklärt, Sie nicht erwarten, dass keines der System-DLLs mit diesem Attribut alle Typen enthalten. Die Assembly.GlobalAssemblyCache Eigenschaft ist ein schneller Weg für System-DLLs zu überprüfen. Als ich versuchte, das auf einem realen Programm fand ich, dass ich 30.101 Arten überspringen könnte und ich habe nur 1.983 Arten überprüfen.
Eine weitere Möglichkeit, Filter ist Assembly.ReferencedAssemblies zu verwenden. Vermutlich, wenn Sie Klassen mit einem bestimmten Attribut wollen, und das Attribut wird in einer bestimmten Assembly definiert, dann kümmern Sie nur über diese Montage und andere Baugruppen, die auf sie verweisen. In meinen Tests half dies etwas mehr als die Global Eigenschaft überprüfen.
I kombiniert beides und haben es sogar noch schneller. Der folgende Code enthält beiden Filter.
string definedIn = typeof(XmlDecoderAttribute).Assembly.GetName().Name;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
// Note that we have to call GetName().Name. Just GetName() will not work. The following
// if statement never ran when I tried to compare the results of GetName().
if ((!assembly.GlobalAssemblyCache) && ((assembly.GetName().Name == definedIn) || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
foreach (Type type in assembly.GetTypes())
if (type.GetCustomAttributes(typeof(XmlDecoderAttribute), true).Length > 0)
Bei der Tragbarer .NET Einschränkungen , sollte der folgende Code funktioniert:
public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies,
Type attributeType )
{
var typesAttributed =
from assembly in assemblies
from type in assembly.DefinedTypes
where type.IsDefined(attributeType, false)
select type;
return typesAttributed;
}
oder für eine große Anzahl von Baugruppen schlaufenzustandsbasierten yield return
verwendet:
public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies,
Type attributeType )
{
foreach (var assembly in assemblies)
{
foreach (var typeInfo in assembly.DefinedTypes)
{
if (typeInfo.IsDefined(attributeType, false))
{
yield return typeInfo;
}
}
}
}
Wir können auf Andrew Antwort verbessern und das Ganze in eine LINQ-Abfrage konvertieren.
public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
{
return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
}