151

Is it possible to add attributes at runtime or to change the value of an attribute at runtime?

Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
Jon Turner
  • 2,932
  • 3
  • 22
  • 20

10 Answers10

71

This really depends on what exactly you're trying to accomplish.

The System.ComponentModel.TypeDescriptor stuff can be used to add attributes to types, properties and object instances, and it has the limitation that you have to use it to retrieve those properties as well. If you're writing the code that consumes those attributes, and you can live within those limitations, then I'd definitely suggest it.

As far as I know, the PropertyGrid control and the visual studio design surface are the only things in the BCL that consume the TypeDescriptor stuff. In fact, that's how they do about half the things they really need to do.

Alex Lyman
  • 15,637
  • 3
  • 38
  • 42
  • 7
    Actually, most data-binding uses `TypeDescriptor` - not just `PropertyGrid`. – Marc Gravell Sep 09 '10 at 05:23
  • 1
    Any workaround to add property-metadata attributes in a Silverlight project (where `TypeDescriptor` and `TypeDescriptionProvider` aren't implemented? – Shimmy Weitzhandler Feb 25 '12 at 22:01
  • 1
    Important to note, TypeDescriptor.GetAttributes() does not handle duplicate attributes. It only selects the last of the attribute type. Ex `[Attr(1), Attr(2), Attr(3)]` only `Attr(3)` is found. – ohmusama May 22 '13 at 17:45
69

Attributes are static metadata. Assemblies, modules, types, members, parameters, and return values aren't first-class objects in C# (e.g., the System.Type class is merely a reflected representation of a type). You can get an instance of an attribute for a type and change the properties if they're writable but that won't affect the attribute as it is applied to the type.

shA.t
  • 16,580
  • 5
  • 54
  • 111
Mark Cidade
  • 98,437
  • 31
  • 224
  • 236
12

You can't. One workaround might be to generate a derived class at runtime and adding the attribute, although this is probably bit of an overkill.

petr k.
  • 8,040
  • 7
  • 41
  • 52
11

Well, just to be different, I found an article that references using Reflection.Emit to do so.

Here's the link: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , you will also want to look into some of the comments at the bottom of the article, because possible approaches are discussed.

torial
  • 13,085
  • 9
  • 62
  • 89
  • 11
    Note that you can create attributes at runtime with the Reflection.Emit classes, BUT you can bind them to classes you have built with the Emit package and not to existing ones. – Panos Sep 24 '08 at 20:50
  • @Hopeless, you may subclass `YourClass` into `YourRuntimeClassWithAttributes`. – Motomotes Sep 16 '19 at 14:23
  • @Motes not sure what you mean, my classes are all defined beforehand, that means all the base classes (that my classes inherit) should also be defined/determined beforehand. I cannot think of any way for it to be involved with something dynamically created using Reflection.Emit. – Hopeless Sep 17 '19 at 04:30
  • 2
    @Hopeless, if you wanted to add attributes dynamically to an existing class `YourClass`, you could subclass it at runtime and generate an identical class with a slightly different name that also has the desired dynamically created attributes, and polymorphism will allow type checking code to still identify your baseclass. – Motomotes Sep 17 '19 at 11:53
4

No, it's not.

Attributes are meta-data and stored in binary-form in the compiled assembly (that's also why you can only use simple types in them).

Thomas Danecker
  • 4,635
  • 4
  • 32
  • 31
3

I don't believe so. Even if I'm wrong, the best you can hope for is adding them to an entire Type, never an instance of a Type.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • 25
    TypeDescriptor.AddAttributes(Object, Attribute[]) adds class-level attributes to the target component instance. – Peter Wone Nov 10 '08 at 12:25
3

If you need something to be able to added dynamically, c# attributes aren't the way. Look into storing the data in xml. I recently did a project that i started w/ attributes, but eventually moved to serialization w/ xml.

Darren Kopp
  • 76,581
  • 9
  • 79
  • 93
  • 2
    maybe it's not a beautiful way but it's the way many other libraries choose to use, and to customize those libraries' behaviors, we have a requirement to play with reflection =)) really a deadlock. – Hopeless May 29 '19 at 18:11
3

Why do you need to? Attributes give extra information for reflection, but if you externally know which properties you want you don't need them.

You could store meta data externally relatively easily in a database or resource file.

Keith
  • 150,284
  • 78
  • 298
  • 434
  • 2
    Boilerplate elimination. Wouldn't it be handy if you could have a class automagically generate attributes based on the code within the class? I'm trying to figure out something like this to reduce boilerplate in SQL CLR objects. Would be easy in other languages ... see http://www.paulgraham.com/avg.html – Duncan Bayne May 31 '11 at 01:30
1

Like mentionned in a comment below by Deczaloth, I think that metadata is fixed at compile time. I achieve it by creating a dynamic object where I override GetType() or use GetCustomType() and writing my own type. Using this then you could...

I tried very hard with System.ComponentModel.TypeDescriptor without success. That does not means it can't work but I would like to see code for that.

In counter part, I wanted to change some Attribute values. I did 2 functions which work fine for that purpose.

        // ************************************************************************
        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
  • 10,996
  • 11
  • 84
  • 119
  • With those methods you change the value of the Attribute, as a separate entity. The Attribute of THE Property remains unchanged! – deczaloth Nov 17 '21 at 11:18
  • That is possible, in fact I already done a dynamic object (where you can add/remove dynamic properties at runtime and it is highly possible my code come from that small library). Sorry for that. – Eric Ouellet Nov 17 '21 at 12:22
0

When faced with this situation, yet another solution might be questioning you code design and search for a more object-oriented way. For me, struggling with unpleasant reflection work arounds is the last resort. And my first reaction to this situation would be re-designing the code. Think of the following code, which tries to solve the problem that you have to add an attribute to a third-party class you are using.

class Employee {} // This one is third-party.

And you have code like

var specialEmployee = new Employee();
// Here you need an employee with a special behaviour and 
// want to add an attribute to the employee but you cannot.

The solution might be extracting a class inheriting from the Employee class and decorating it with your attribute:

[SpecialAttribute]
class SpecialEmployee : Employee 
{
}

When you create an instance of this new class

var specialEmployee = new SpecialEmployee();

you can distinguish this specialEmployee object from other employee objects. If appropriate, you may want to make this SpecialEmployee a private nested class.

Mustafa Özçetin
  • 1,893
  • 1
  • 14
  • 16