Dienstag, 10. Januar 2012

Set Properties to ReadOnly at runtime

Lately I needed to set the ReadOnly Attribute of a Property in C# at runtime. I wanted to change the IsReadOnly Property dynamically. The problem was that the IsReadOnly Property of the ReadOnlyAttribute does not contain a setter meaning that it is ReadOnly...

After doing a lot of searching I stumbled on this Post by David Morton. Here he describes how to use Reflection to edit internal fields.

I created a Method that uses Reflection to change the IsReadOnly Property of the ReadOnlyAttribute.
     /// <summary>  
     /// Uses reflection to set the readonly attribute on a property.  
     /// This value is set to the class definition and not to the instance.   
     /// If it is set once than it has to be reset again to get the default.  
     /// </summary>  
     /// <param name="instance">instance containing the property</param>  
     /// <param name="property">the property to set the readonly value</param>  
     /// <param name="value">value to set to the property</param>  
     private static void SetReadOnly(object instance, string property, bool value)  
     {  
       // Create a PropertyDescriptor for the Property by calling the static 
       // GetProperties on TypeDescriptor.  
       PropertyDescriptor descriptor = 
                        TypeDescriptor.GetProperties(instance.GetType())[property];  
       // Fetch the ReadOnlyAttribute from the descriptor.   
       ReadOnlyAttribute attrib = 
                       (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];  
       // Get the internal isReadOnly field from the ReadOnlyAttribute using reflection.   
       System.Reflection.FieldInfo isReadOnly = 
                       attrib.GetType().GetField("isReadOnly", 
                              System.Reflection.BindingFlags.NonPublic | 
                              System.Reflection.BindingFlags.Instance);  
       // Using Reflection, set the internal isReadOnly field.   
       isReadOnly.SetValue(attrib, value);   
     }  

In this excample I want to change the value of the ReadOnlyAttribute of the TestProperty.
   class PropertyTest  
   {  
     string _testProperty;  
     [ReadOnly(false)]  
     public string TestProperty  
     {  
       get  
       {  
         return _testProperty;  
       }  
       set  
       {  
         _testProperty = value;  
       }  
     }  
   }  
Now I can pass a instance and make any Property I want ReadOnly or Readable again.
     public void Edit()  
     {  
       var instance = new PropertyTest();  
       SetReadOnly(instance, "TestProperty", true);  
     }  
A big drawback about this is that it breaks encapsulation.
Another problem with this approach ist that it changes the Attribute in the Class definition and not only in the instance. If a new object is created the ReadOnlyAttribute of Property has the value that was set previously. In that case the Attribute has to be set every time a new Instance is created.

Keine Kommentare: