C# – Obtenir toutes les classes avec un attribut personnalisé

C# – Obtenir toutes les classes avec un attribut personnalisé

Pour obtenir toutes les classes avec un attribut personnalisé, obtenez d'abord tous les types dans l'assembly, puis utilisez IsDefined(customAttributeType) pour filtrer les types :

using System.Reflection;

var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsDefined(typeof(ApiControllerAttribute)));
Code language: C# (cs)

Cela recherche des classes dans l'assembly actuel qui ont l'attribut [ApiController], comme cette classe de contrôleur :

[ApiController]
[Route("[controller]")]
public class RandomNumberController : ControllerBase
{}
Code language: C# (cs)

Ceci est utile dans plusieurs scénarios, par exemple lorsque vous souhaitez enregistrer des informations au démarrage ou lorsque vous associez automatiquement des classes.

Dans cet article, je vais montrer comment rechercher tous les assemblys, comment gérer les attributs personnalisés hérités et comment obtenir les valeurs des attributs personnalisés (au lieu de simplement vérifier s'ils existent).

Rechercher dans tous les assemblages chargés

Pour rechercher tous les assemblys, utilisez AppDomain.CurrentDomain.GetAssemblies() pour obtenir un tableau des assemblys actuellement chargés, puis parcourez les assemblys et utilisez GetType() + IsDefined() pour obtenir toutes les classes qui ont un attribut personnalisé spécifique.

La requête Linq suivante simplifie cela :

var types = from assembly in AppDomain.CurrentDomain.GetAssemblies()
			from type in assembly.GetTypes()
			where type.IsDefined(typeof(ApiControllerAttribute))
			select type;
Code language: C# (cs)

C'est un cas où la syntaxe de la requête Linq est en fait plus facile à comprendre que la syntaxe de la méthode :

AppDomain.CurrentDomain.GetAssemblies()
	.SelectMany(a => a.GetTypes().Where(t => t.IsDefined(typeof(ApiControllerAttribute))));
Code language: C# (cs)

Si vous voulez regarder dans les assemblys qui ne sont pas chargés, vous pouvez utiliser l'approche MetadataReader.

Attributs personnalisés hérités

Les attributs personnalisés peuvent être hérités. Prenons l'exemple suivant :

[ApiController]
[Route("[controller]")]
public abstract class ApiControllerBase : ControllerBase
{ }

public class RandomNumberController : ApiControllerBase
{}
Code language: C# (cs)

L'attribut [ApiController] est défini sur la classe abstraite ApiControllerBase et est hérité par la classe RandomNumberController. La recherche de classes avec l'attribut [ApiController] renverrait ces deux classes par défaut, ce qui peut être indésirable. Je vais montrer comment filtrer la requête ci-dessous.

Filtrer les classes abstraites

Il est peu probable que vous vouliez réellement que les classes abstraites soient renvoyées dans la recherche. Vous pouvez les filtrer en vérifiant la propriété Type.IsAbstract, comme ceci :

assembly.GetTypes().Where(t => t.IsDefined(typeof(ApiControllerAttribute)) && !t.IsAbstract)
Code language: C# (cs)

Cela renverrait uniquement la classe RandomNumberController, et non la classe abstraite ApiControllerBase.

Filtrez les classes qui ont hérité de l'attribut personnalisé

Par défaut, IsDefined(customAttributeType) renverra true même si l'attribut personnalisé a été hérité. C'est un bon comportement par défaut, car c'est généralement ce que vous souhaitez.

Cependant, il peut y avoir des scénarios dans lesquels vous souhaitez filtrer les classes qui n'ont que l'attribut personnalisé parce qu'elles en ont hérité. Pour cela, vous pouvez passer en faux pour l'héritage paramètre :

assembly.GetTypes().Where(t => t.IsDefined(typeof(ApiControllerAttribute), inherit: false))
Code language: C# (cs)

Étant donné que la classe RandomNumberController hérite de l'attribut [ApiController] de la classe ApiControllerBase et ne le définit pas directement, il sera filtré.

Obtenir les valeurs des attributs personnalisés

Lorsqu'un attribut personnalisé a des valeurs, vous souhaiterez probablement examiner les valeurs. Par exemple, l'attribut [Route] est déclaré avec une chaîne de modèle de route.

[ApiController]
[Route("[controller]")]
public class RandomNumberController : ControllerBase
{}
Code language: C# (cs)

Pour obtenir les valeurs d'attribut personnalisé, vous pouvez utiliser GetCustomAttribute() pour obtenir l'objet d'attribut personnalisé :

var assembly = Assembly.GetExecutingAssembly();

foreach (var type in assembly.GetTypes())
{
	var routeAttribute = type.GetCustomAttribute<RouteAttribute>();

	if (routeAttribute != null)
	{
		Console.WriteLine($"Controller={type.Name} RouteTemplate={routeAttribute.Template}");
	}
}
Code language: C# (cs)

Si l'attribut personnalisé n'est pas défini sur le type, il renvoie null. Sinon, il renvoie l'objet d'attribut personnalisé et vous pouvez consulter ses propriétés.

Cela génère ce qui suit :

Controller=RandomNumberController RouteTemplate=[controller]Code language: plaintext (plaintext)