Question basé sur MSDN example. Supposons que nous ayons des classes C# avec HelpAttribute dans une application de bureau autonome. Est-il possible d'énumérer toutes les classes avec un tel attribut? Est-ce logique de reconnaître les classes de cette façon? L'attribut personnalisé serait utilisé pour lister les options de menu possibles, la sélection de l'élément entraînera l'affichage à l'écran de cette classe. Le nombre de classes/items augmentera lentement, mais de cette façon nous pouvons éviter de les énumérer tous ailleurs, je pense.Comment énumérer toutes les classes avec l'attribut de classe personnalisée?
Répondre
Oui, absolument. En utilisant la réflexion:
static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
foreach(Type type in assembly.GetTypes()) {
if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
yield return type;
}
}
}
D'accord, mais dans ce cas, nous pouvons le faire de façon déclarative selon la solution de casperOne. C'est agréable de pouvoir utiliser le rendement, c'est encore mieux de ne pas avoir à le faire :) –
J'aime LINQ. J'adore, en fait. Mais il faut une dépendance sur .NET 3.5, ce qui n'est pas le cas du rendement. En outre, LINQ se décompose finalement essentiellement en la même chose que le rendement du rendement. Alors qu'avez-vous gagné? Une syntaxe C# particulière, c'est une préférence. –
Donc utilise le rendement au lieu d'écrire votre propre énumérateur ... –
Eh bien, il vous faudrait énumérer toutes les classes dans toutes les assemblées qui sont chargés dans le domaine de l'application actuelle. Pour ce faire, appelez le GetAssemblies
method sur l'instance AppDomain
pour le domaine d'application actuel. À partir de là, vous appelez GetExportedTypes
(si vous voulez uniquement les types publics) ou GetTypes
sur chaque Assembly
pour obtenir les types qui sont contenus dans l'assembly.
Ensuite, vous appelez le GetCustomAttributes
method sur chaque instance Type
, en passant le type de l'attribut que vous souhaitez trouver.
Vous pouvez utiliser LINQ pour simplifier pour vous:
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>() };
La requête ci-dessus vous obtiendrez chaque type avec votre attribut appliqué, ainsi que l'instance de l'attribut (s) qui lui est attribué.
Notez que si vous avez un grand nombre d'assemblys chargés dans votre domaine d'application, cette opération peut être coûteuse. Vous pouvez utiliser Parallel LINQ pour réduire le temps de l'opération, comme ceci:
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>() };
Filtrage sur un Assembly
spécifique est simple:
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>() };
Et si l'assemblée a un grand nombre de types dans ce , alors vous pouvez utiliser à nouveau Parallel LINQ:
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>() };
L'énumération de tous les types dans les assemblys _all_ chargés serait très lente et ne vous rapportera pas grand-chose. C'est aussi potentiellement un risque pour la sécurité. Vous pouvez probablement prédire quels assemblages contiendront les types qui vous intéressent. Il vous suffit d'énumérer les types dans ceux-ci. –
@Andrew Arnott: Oui, mais c'est ce qui a été demandé. Il est assez facile d'élaguer la requête pour un assemblage particulier. Cela a également l'avantage de vous donner la correspondance entre le type et l'attribut. – casperOne
Merci pour la solution, mais je ne suis pas vraiment habitué à tout ce truc LINQ :-) – tomash
Comme déjà indiqué, la réflexion est la voie à suivre. Si vous appelez cela fréquemment, je suggère fortement de mettre en cache les résultats, car la réflexion, en particulier l'énumération à travers chaque classe, peut être assez lente.
C'est un extrait de mon code qui passe par tous les types dans toutes les assemblées chargées:
// 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.
}
}
}
Autres réponses référence GetCustomAttributes.Ajout de celui-ci comme un exemple d'utilisation IsDefined
Assembly assembly = ...
var typesWithHelpAttribute =
from type in assembly.GetTypes()
where type.IsDefined(typeof(HelpAttribute), false)
select type;
Je crois que c'est la bonne solution qui utilise la méthode prévue par le cadre. –
En cas de Portable .NET limitations, le code devrait fonctionner:
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;
}
ou pour un grand nombre d'assemblages en utilisant l'état de la boucle à base yield return
:
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;
}
}
}
}
Il s'agit d'une amélioration des performances par rapport à la solution acceptée. Itérer si toutes les classes peuvent être lentes parce qu'il y en a tellement. Parfois, vous pouvez filtrer un assemblage entier sans regarder aucun de ses types. Par exemple, si vous recherchez un attribut que vous avez déclaré vous-même, vous ne vous attendez pas à ce que les DLL système contiennent des types avec cet attribut. La propriété Assembly.GlobalAssemblyCache est un moyen rapide de vérifier les DLL système. Quand j'ai essayé ceci sur un vrai programme j'ai trouvé que je pourrais sauter 30.101 types et je dois seulement vérifier 1.983 types.
Une autre façon de filtrer est d'utiliser Assembly.ReferencedAssemblies. Vraisemblablement, si vous voulez des classes avec un attribut spécifique, et que cet attribut est défini dans un assembly spécifique, alors vous vous souciez uniquement de cet assembly et des autres assemblys qui le référencent. Dans mes tests, cela a aidé légèrement plus que la vérification de la propriété GlobalAssemblyCache. J'ai combiné les deux et je l'ai obtenu encore plus rapidement. Le code ci-dessous inclut les deux filtres.
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)
- 1. Comment utiliser une classe de configuration personnalisée pour web.config avec les classes Linq dbml
- 2. La super classe de toutes les classes d'événements
- 3. Comment énumérer toutes les variables définies dans un script groovy
- 4. Énumérer toutes les poignées de fenêtre sur le bureau
- 5. Comment énumérer les noms de colonnes avec NHibernate?
- 6. Liste toutes les sous-classes avec des noms entièrement qualifiés
- 7. PHP afficher les noms de toutes les classes déclarées?
- 8. Comment trouver toutes les classes qui implémentent une interface donnée?
- 9. importer toutes les variables de classe parente
- 10. Trouver toutes les classes avec un attribut particulier
- 11. Comment obtenez-vous toutes les propriétés d'une classe et de ses classes de base (dans la hiérarchie) avec Reflection? (C#)
- 12. Comment parcourir toutes les propriétés d'une classe?
- 13. Python - Itérer sur toutes les classes
- 14. Liste toutes les classes de base dans une hiérarchie de classe donnée?
- 15. Comment énumérer les disques durs
- 16. Générer toutes les permutations possibles d'une classe
- 17. Énumérer les fonctions DLL?
- 18. Liste toutes les classes qui existent actuellement
- 19. Existe-t-il une méthode .NET pour énumérer toutes les imprimantes réseau disponibles?
- 20. Évitant Héritant Toutes les choses de la classe de base
- 21. Supprime toutes les classes qui commencent par une certaine chaîne
- 22. sérialisation classe dérivée personnalisée
- 23. Extraction de toutes les classes d'un espace de noms spécifique
- 24. Sur Windows XP, comment énumérer toutes les fenêtres affichées par le système (C#)
- 25. Comment énumérer tous les éléments ActiveX avec WMI?
- 26. Comment énumérer les fichiers + dossiers récursivement avec System.IO.Directory.GetFiles
- 27. Conception d'une classe aléatoire personnalisée
- 28. À quel point, en théorie, est-ce que chaque classe inclurait toutes les autres classes?
- 29. Énumérer les partages réseau Windows et toutes les autorisations personnalisées sur ou à l'intérieur de
- 30. Énumérer les arbres de recherche
Un exemple de lien MSDN est un lien mort. – MadTigger