Accès Efficace aux Attributs Personnalisés sur les Valeurs d’Enum en C#

Si vous développez des applications en C#, vous pourriez parfois rencontrer le besoin de récupérer des attributs personnalisés associés aux valeurs d’énumération. Cette tâche peut sembler simple, surtout pour ceux qui ne sont pas familiers avec la réflexion et la génération de code IL. Cependant, simplement convertir les valeurs d’énumération en chaînes (leurs noms) peut souvent entraîner des problèmes de performance. Dans cet article, nous explorerons une méthode propre et efficace pour récupérer ces attributs sans les tracas des conversions de chaînes.

Comprendre le Problème

Commençons par un exemple rapide où nous définissons une énumération ornée d’attributs personnalisés :

public enum MyEnum {
    [CustomInfo("Ceci est un attribut personnalisé")]
    None = 0,

    [CustomInfo("Ceci est un autre attribut")]
    ValueA,

    [CustomInfo("Ceci a un drapeau supplémentaire", AllowSomething = true)]
    ValueB,
}

Dans cet extrait, nous avons une énumération nommée MyEnum, qui contient plusieurs valeurs, chacune associée à un attribut CustomInfo détaillant des informations supplémentaires liées à chaque membre de l’énumération.

Le Défi

Le principal défi se présente lorsque nous devons récupérer ces attributs CustomInfo à partir d’une instance de l’énumération. La première méthode qui vient à l’esprit est d’utiliser le code suivant :

public CustomInfoAttribute GetInfo(MyEnum enumInput) {
    Type typeOfEnum = enumInput.GetType();
    FieldInfo fi = typeOfEnum.GetField(enumInput.ToString());
    return fi.GetCustomAttributes(typeof(CustomInfoAttribute), false).
        FirstOrDefault() as CustomInfoAttribute;
}

Bien que cette fonction récupère efficacement l’attribut, elle souffre d’inefficacités de performance dues à l’utilisation de enumInput.ToString(), qui convertit la valeur d’énumération en sa représentation sous forme de chaîne.

Une Meilleure Approche : Utiliser la Génération de Code IL

Heureusement, il existe une méthode plus efficace pour accéder aux attributs personnalisés sans convertir les énumérations en chaînes en utilisant la génération de code IL. En employant des méthodes dynamiques et l’ILGenerator, nous réduisons le surcoût et améliorons les performances. Ci-dessous, nous décrivons comment créer un délégué qui aide à accéder rapidement aux propriétés, pouvant être adapté pour la récupération d’attributs également.

Étapes pour Mettre en Œuvre la Génération de Code IL

  1. Créer un Délégué : Définissez un délégué qui prend un objet et retourne un objet. Cela nous permettra de créer un accesseur de propriété rapide.

    public delegate object FastPropertyGetHandler(object target);
    
  2. Émettre du Code IL : En utilisant l’ILGenerator, nous pouvons générer une méthode IL qui charge l’objet cible sur la pile, appelle la méthode getter et gère correctement la valeur de retour.

    private static void EmitBoxIfNeeded(ILGenerator ilGenerator, System.Type type) {
        if (type.IsValueType) {
            ilGenerator.Emit(OpCodes.Box, type);
        }
    }
    
  3. Obtenir le Getter de Propriété : Maintenant, nous créons une méthode pour générer une méthode dynamique et construire le code IL nécessaire pour créer un accesseur de propriété rapide :

    public static FastPropertyGetHandler GetPropertyGetter(PropertyInfo propInfo) {
        DynamicMethod dynamicMethod = new DynamicMethod(
            string.Empty, 
            typeof(object), 
            new Type[] { typeof(object) },
            propInfo.DeclaringType.Module);
    
        ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.EmitCall(OpCodes.Callvirt, propInfo.GetGetMethod(), null);
        EmitBoxIfNeeded(ilGenerator, propInfo.PropertyType);
        ilGenerator.Emit(OpCodes.Ret);
        FastPropertyGetHandler getter = (FastPropertyGetHandler)dynamicMethod.CreateDelegate(typeof(FastPropertyGetHandler));
        return getter;
    }
    

Conclusion

En mettant en œuvre la méthode décrite ci-dessus, vous pouvez effectivement réduire le surcoût associé à la récupération d’attributs à partir des valeurs d’énumération. Non seulement cette approche élimine les conversions de chaînes inutiles, mais elle produit également de meilleures performances à l’exécution—essentielles pour les applications qui exigent efficacité.

Incorporer ces techniques dans vos projets C# vous aidera à naviguer facilement dans les attributs d’énumération et permettra un code plus propre et plus facile à maintenir. Bon codage !