84
//Get PropertyDescriptor object for the given property name
var propDesc = TypeDescriptor.GetProperties(typeof(T))[propName];

//Get FillAttributes methodinfo delegate
var methodInfo = propDesc.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public |
                                                      BindingFlags.NonPublic)
    .FirstOrDefault(m => m.IsFamily || m.IsPublic && m.Name == "FillAttributes");

//Create Validation attribute
var attribute = new RequiredAttribute();
var  attributes= new ValidationAttribute[]{attribute};

//Invoke FillAttribute method
methodInfo.Invoke(propDesc, new object[] { attributes });

Hi I am trying to add Validation attribute at runtime using the above code. However I am getting the below exception:

Collection was of a fixed size

Shoe
  • 74,840
  • 36
  • 166
  • 272
Thiru kumaran
  • 1,262
  • 1
  • 10
  • 10

5 Answers5

230

Don't let someone tell you that you can't do it. You can run for president if you want :-)

For your convenience, this is a fully working example

public class SomeAttribute : Attribute
{
    public SomeAttribute(string value)
    {
        this.Value = value;
    }

    public string Value { get; set; }
}

public class SomeClass
{
    public string Value = "Test";
}

[TestMethod]
public void CanAddAttribute()
{
    var type = typeof(SomeClass);

    var aName = new System.Reflection.AssemblyName("SomeNamespace");
    var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
    var mb = ab.DefineDynamicModule(aName.Name);
    var tb = mb.DefineType(type.Name + "Proxy", System.Reflection.TypeAttributes.Public, type);

    var attrCtorParams = new Type[] { typeof(string) };
    var attrCtorInfo = typeof(SomeAttribute).GetConstructor(attrCtorParams);
    var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { "Some Value" });
    tb.SetCustomAttribute(attrBuilder);

    var newType = tb.CreateType();
    var instance = (SomeClass)Activator.CreateInstance(newType);

    Assert.AreEqual("Test", instance.Value);
    var attr = (SomeAttribute)instance.GetType()
        .GetCustomAttributes(typeof(SomeAttribute), false)
        .SingleOrDefault();
    Assert.IsNotNull(attr);
    Assert.AreEqual(attr.Value, "Some Value");
}
Pang
  • 9,564
  • 146
  • 81
  • 122
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • 153
    Thumbs up for the motivational speak! – Sjoerd222888 Sep 10 '15 at 09:38
  • 1
    Hmm, seems we can't use this technique for enum members. I get an error saying the parent class is sealed... – Jerther Dec 16 '15 at 15:15
  • 1
    @Jerther, the code creates a proxy class which inherits the base class at runtime. So yes, this does not work for sealed classes. – Jürgen Steinblock Dec 17 '15 at 06:55
  • 9
    This adds an attribute to the class instead of a property of the class, right? – Denny Mar 22 '17 at 13:21
  • 4
    @Denny That's true but the concept will be the same. Property will have to be virtual for this to work and you will neeed to emit some IL code. This [question](http://stackoverflow.com/q/8720275/98491) should help you out. – Jürgen Steinblock Mar 23 '17 at 14:00
  • The code being a lill abstruse, in your example, what's being added to what now? – jeromej Apr 29 '18 at 13:24
  • 7
    @JeromeJ The code dynamically creates a new class that inherits from `SomeClass` adds the `class level` attribute `SomeAttribute`, creates an instance and verifies the result. Just like you could do statically with `[SomeAttribute("Some Value")] public class SomeClassProxy : SomeClass { }; var instance = new SomeClassProxy();` – Jürgen Steinblock May 02 '18 at 13:24
  • A bit late here but the solution was quoted in another answer. So, instance variable is in fact an instance of a class which inherits from the original. And everything that uses reflection (and this is how custom attributes are used) will see no attributes applied to typeof(SomeClass) or instance.GetType(), right? – Alexander Christov Apr 23 '20 at 10:18
  • @AlexanderChristov Yes and no. While SomeClass isn't modified `instance.GetType()` will be `SomeClassProxy` and this will most likely work for code that takes a type or object as an argument for attribute querying. However, a hard coded `typeof(SomeClass).GetCustomAttributes(...)` won't work. – Jürgen Steinblock Apr 24 '20 at 11:15
  • 1
    @Jurgen: Yes, in some case this might work, I do agree. However, this is a huge deviation from normal coding practices (shortest distance to target), this is why I would reconsider the need to dynamically inject an attribute. This is an excellent example of a suggestion to rethink and reformulate the problem - .NET is almost perfect in this respect. – Alexander Christov Apr 25 '20 at 05:28
  • @AlexanderChristov Obviously, you can't just change an existing class without recompiling. If you have a class `A` from an assembly that is out of your control and you have a method `B` out of your control that expects some attribute metadata, this is a working example. Proxy classes are quite common pattern, i.e. EntityFramework does this to implement lazy loading. – Jürgen Steinblock Apr 27 '20 at 12:50
  • in .NET 6 this didn't work for me when I was trying to use reflection AppDomain.CurrentDomain.GetAssemblies() - I got an assembly not found and apparently it's not supported in .NET Core - anyone got the same? – Ricardo Rodrigues Aug 26 '22 at 09:39
  • I just tested it with .NET core 6 on windows an linux and `AppDomain.CurrentDomain.GetAssemblies()` returns a result. Even more, the whole code runs fine (except `AppDomain.CurrentDomain.DefineDynamicAssembly` has to be replaced with `System.Reflection.Emit.AssemblyBuilder.DefineDynamicAssembly`. So no issue here. – Jürgen Steinblock Aug 26 '22 at 12:56
  • 1
    Seems like the downvoted answer is actually the answer then. This is creating a new class, not taking the existing class and adding an attribute to a property. – Paul Mar 07 '23 at 17:38
8

The top answer is awesome. Recently, a library has been developed, that abstracts away all of that complexity, and gives you something as simple as this:

var attributeType = typeof(CustomAAttribute);
var attributeParams = new object[] { "Jon Snow" };
var typeExtender = new TypeExtender("ClassA");
typeExtender.AddProperty("IsAdded", typeof(bool), attributeType, attributeParams);

To work with. details of how to install and use the library can be found here

Disclaimer: I developed this library and I've been using it for a lot of projects and it works like magic

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Ndubuisi Jr
  • 461
  • 6
  • 13
5

Use FastDeepCloner which I developed.

public class test{
    public string Name{ get; set; }
}

var prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
prop.Attributes.Add(new JsonIgnoreAttribute());
// now test and se if exist 
prop = DeepCloner.GetFastDeepClonerProperties(typeof(test)).First();
bool containAttr = prop.ContainAttribute<JsonIgnoreAttribute>()
// or 
JsonIgnoreAttribute myAttr = prop.GetCustomAttribute<JsonIgnoreAttribute>();
Pang
  • 9,564
  • 146
  • 81
  • 122
Alen.Toma
  • 4,684
  • 2
  • 14
  • 31
0

It is not working because the FillAttributes method expects a parameter of the IList type and you are passing an array. Below is the implementation of MemberDescriptor.FillAttributes:

protected virtual void FillAttributes(IList attributeList) { 
    if (originalAttributes != null) {
        foreach (Attribute attr in originalAttributes) {
            attributeList.Add(attr);
        } 
    }
}

As you can see, FillAttributes just fills the attributeList parameter with all attributes of your property. And to make your code work, change the var attributes= new ValidationAttribute[]{attribute}; line with:

var attributes = new ArrayList { attribute };

This code has nothing with adding attributes to property of type at runtime. This is "adding attribute to the PropertyDescriptor" extracted from type and has no sense unless you are trying to build a type at runtime which is based on an already existing type.

Pang
  • 9,564
  • 146
  • 81
  • 122
Alexander Manekovskiy
  • 3,185
  • 1
  • 25
  • 34
-13

It is not possible to add Attributes in run-time. Attributes are static and cannot be added or removed.

Similar questions:

Community
  • 1
  • 1
Artless
  • 4,522
  • 1
  • 25
  • 40