Search This Blog

Loading...

Monday, February 23, 2009

How to Set Attribute Value at Runtime-- and How to Work around a Silly Bug

Changing a property's attribute at runtime is generally frowned upon. After all, attributes are just metadata and should remain unchanged after the they are compiled. But sometimes you have to do it, regardless of what the "best practices" are saying.


In my projects, there are a lot of times when I have to reuse the components that I have built. I often found that I needed to do a slight tinkering to the components to get them suit my new applications. One of the most common scenario I found was that I had to hide some of the properties of a class. Given that I couldn't modify the base components, the only option I had is to change the attribute values at runtime, at a different assembly.


Supposed that  we have a student class located in the base assembly:
public class Student
    {
        public string Name
        {
            get;
            set;
        }

        public string Address
        {
            get;
            set;
        }
        
    }

and we want to make the Address property Readonly via reflection in another assembly, we can thus implement the following function to accomplish this goal:
private void SetPropertyGrid()
        {

            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(typeof(Student))["Address"];
            ReadOnlyAttribute attrib = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
            FieldInfo isReadOnly = attrib.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
            isReadOnly.SetValue(attrib,true);
            propertyGrid1.SelectedObject = new Student();
        }

Here's the result:


All the properties are set to readonly, including Name. The code seems correct, but the behaviors are not. Surprised?


I think this is a .Net bug. Microsoft confirmed that this is indeed a bug . In the mean time, you have to explicitly set the ReadOnly attribute to false in order to get the code working:
public class Student
    {
        [ReadOnly(false)]
        public string Name
        {
            get;
            set;
        }
        [ReadOnly(false)]
        public string Address
        {
            get;
            set;
        }
        
    }

Yeah, you have to change the base components. But there is no choice. And this is the result after modification:

.Net is indeed big, and weird, don't you think so?

1 comment:

Anonymous said...

Code doesnot work for me.

AccSecurityAttribute acc = (AccSecurityAttribute)Attribute.GetCustomAttribute(typeof(Form2), typeof(AccSecurityAttribute));
ReadOnlyAttribute attr = (ReadOnlyAttribute)Attribute.GetCustomAttribute(typeof(Form2), typeof(ReadOnlyAttribute));

FieldInfo FI = attr.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
FI.SetValue(attr, true);

Altough it sets but when i retrive it takes old value only .