Les attributs peuvent-ils être ajoutés dynamiquement en C #?

143

Est-il possible d'ajouter des attributs lors de l'exécution ou de modifier la valeur d'un attribut lors de l'exécution?

Jon Turner
la source

Réponses:

67

Les attributs sont des métadonnées statiques. Les assemblys, modules, types, membres, paramètres et valeurs de retour ne sont pas des objets de première classe en C # (par exemple, la System.Typeclasse est simplement une représentation reflétée d'un type). Vous pouvez obtenir une instance d'un attribut pour un type et modifier les propriétés si elles sont accessibles en écriture, mais cela n'affectera pas l'attribut tel qu'il est appliqué au type.

Mark Cidade
la source
68

Cela dépend vraiment de ce que vous essayez d'accomplir.

Le truc System.ComponentModel.TypeDescriptor peut être utilisé pour ajouter des attributs à des types, des propriétés et des instances d'objet, et il a la limitation que vous devez l'utiliser pour récupérer ces propriétés également. Si vous écrivez le code qui consomme ces attributs et que vous pouvez vivre dans ces limites, je le suggérerais certainement.

Autant que je sache, le contrôle PropertyGrid et la surface de conception de Visual Studio sont les seuls éléments de la BCL qui consomment les éléments TypeDescriptor. En fait, c'est ainsi qu'ils font environ la moitié des choses dont ils ont vraiment besoin.

Alex Lyman
la source
7
En fait, la plupart des utilisations de liaison de données TypeDescriptor- pas seulement PropertyGrid.
Marc Gravell
1
Une solution de contournement pour ajouter des attributs de métadonnées de propriété dans un projet Silverlight (où TypeDescriptoret TypeDescriptionProviderne sont-elles pas implémentées?
Shimmy Weitzhandler
1
Important à noter, TypeDescriptor.GetAttributes () ne gère pas les attributs en double. Il sélectionne uniquement le dernier du type d'attribut. Ex [Attr(1), Attr(2), Attr(3)]seul Attr(3)est trouvé.
ohmusama
11

Vous ne pouvez pas. Une solution de contournement pourrait être de générer une classe dérivée au moment de l'exécution et d'ajouter l'attribut, bien que ce soit probablement un peu excessif.

petr k.
la source
10

Eh bien, juste pour être différent, j'ai trouvé un article qui fait référence à l'utilisation de Reflection.Emit pour le faire.

Voici le lien: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , vous voudrez également examiner certains des commentaires au bas de l'article, car les approches possibles sont discutées.

torial
la source
10
Notez que vous pouvez créer des attributs au moment de l'exécution avec les classes Reflection.Emit, MAIS vous pouvez les lier aux classes que vous avez construites avec le package Emit et non à celles existantes.
Panos
quelle réponse inutile =)) nous nous soucions tous des classes existantes ici, pas des classes dynamiques.
Hopeless
@Hopeless, vous pouvez sous-classer YourClassdans YourRuntimeClassWithAttributes.
Motes
@Motes ne sais pas ce que vous voulez dire, mes classes sont toutes définies à l'avance, cela signifie que toutes les classes de base (dont mes classes héritent) doivent également être définies / déterminées à l'avance. Je ne vois aucun moyen pour qu'il soit impliqué dans quelque chose de créé dynamiquement à l'aide de Reflection.Emit.
Hopeless
1
@Hopeless, si vous souhaitez ajouter des attributs dynamiquement à une classe existante YourClass, vous pouvez la sous- classer au moment de l'exécution et générer une classe identique avec un nom légèrement différent qui a également les attributs créés dynamiquement souhaités, et le polymorphisme permettra au code de vérification de type de toujours identifier votre classe de base.
Motes le
4

Non ce n'est pas.

Les attributs sont des méta-données et stockés sous forme binaire dans l'assembly compilé (c'est aussi pourquoi vous ne pouvez utiliser que des types simples).

Thomas Danecker
la source
3

Je ne crois pas. Même si je me trompe, le mieux que vous puissiez espérer est de les ajouter à un type entier, jamais à une instance d'un type.

Joël Coehoorn
la source
22
TypeDescriptor.AddAttributes (Object, Attribute []) ajoute des attributs de niveau classe à l'instance de composant cible.
Peter Wone
3

Si vous avez besoin de quelque chose pour pouvoir être ajouté dynamiquement, les attributs c # ne sont pas la solution. Regardez dans le stockage des données au format XML. J'ai récemment fait un projet que j'ai commencé avec des attributs, mais qui est finalement passé à la sérialisation avec xml.

Darren Kopp
la source
1
ce n'est peut-être pas une belle façon, mais c'est la façon dont de nombreuses autres bibliothèques choisissent d'utiliser, et pour personnaliser les comportements de ces bibliothèques, nous devons jouer avec la réflexion =)) vraiment une impasse.
Hopeless
3

Pourquoi en avez-vous besoin? Les attributs donnent des informations supplémentaires pour la réflexion, mais si vous savez de l'extérieur quelles propriétés vous voulez, vous n'en avez pas besoin.

Vous pouvez stocker des métadonnées en externe assez facilement dans une base de données ou un fichier de ressources.

Keith
la source
1
Élimination de la chaudière. Ne serait-il pas pratique de faire en sorte qu'une classe génère automatiquement des attributs basés sur le code de la classe? J'essaie de comprendre quelque chose comme ça pour réduire le passe-partout dans les objets SQL CLR. Ce serait facile dans d'autres langues ... voir paulgraham.com/avg.html
Duncan Bayne
1

J'ai essayé très dur avec System.ComponentModel.TypeDescriptor sans succès. Cela ne veut pas dire que cela ne peut pas fonctionner, mais j'aimerais voir du code pour cela.

En contrepartie, je voulais changer certaines valeurs d'attribut. J'ai fait 2 fonctions qui fonctionnent bien à cet effet.

        // ************************************************************************
        public static void SetObjectPropertyDescription(this Type typeOfObject, string propertyName,  string description)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("description", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, description);
                }
            }
        }

        // ************************************************************************
        public static void SetPropertyAttributReadOnly(this Type typeOfObject, string propertyName, bool isReadOnly)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, isReadOnly);
                }
            }
        }
Eric Ouellet
la source