System Attributes can be used to simply tag and retrieve object properties and methods in particular context.
In my case here, I wanted to be able to know which properties are contained in objects in order to be able to dynamically assign or change their values using simple text entries in a database.
I created an (empty) Attribute and named it xSysPropertyAttribute:
[global::System.AttributeUsage(
AttributeTargets.Property,
Inherited = false,
AllowMultiple = true)]
public sealed class xSysPropertyAttribute : Attribute
{
public xSysPropertyAttribute()
{
}
}
I then used this attribute to ‘tag’ the desired objects’ properties:
[xSysProperty]
public bool BoolProperty
{
get { return m_bool_property; }
set { m_bool_property = value; }
}
[xSysProperty]
public string StringProperty
{
get { return m_string_property; }
set { m_string_property = value; }
}
Using System.Reflection, I, now, can retrieve these properties and dynamically list them:
foreach (PropertyInfo p in obj.GetType().GetProperties())
{
bool has_sys_attribute = Attribute.IsDefined( p, typeof(xSysProperty), false);
if(has_sys_attribute)
{
// Do something with this property
}
}
We can use this simple code to dynamically build lists of object’s (tagged) properties.
Once the list of the desired properties has been built, we can now read and modify their values (using user input, xml, text files or database records):
Example of Read the property’s value:
public object GetValue(MyObject obj, string property_name)
{
if( obj == null || string.IsNullOrEmpty( property_name))
return null;
PropertyInfo pinfo = this[property_name];
if( pinfo == null)
return null;
return pinfo.GetValue(obj, null);
}
Example of Set the property’s value:
public bool SetValue(MyObject obj, string property_name, object value)
{
if( obj == null)
return false;
PropertyInfo pinfo = this[property_name];
if(pinfo == null)
return false;
// is this a readonly property?
if(pinfo.CanWrite == false)
return false;
// get the property's data type
Type property_type = pinfo.PropertyType;
object new_value;
try
{
// try to convert the given value to property's data type
new_value = Convert.ChangeType( value, property_type);
}
catch (Exception)
{
return false;
}
try
{
// try to assign the converted value to the object
pinfo.SetValue( obj, new_value, null);
}
catch (Exception)
{
return false;
}
return true;
}
Still we have another problem: we need a solid link between listed properties and the objects on which we may get and set properties’ values. i.e. we need to be sure that a property is actually part of the target object without having to check this each time a get or set is requested.
Here comes one the benefits of template classes.
We can implement our pattern using a template list like the following:
public class xBuiltinPropertiesListBase<PROPERTY, OBJ_TYPE, PROPERTY_SYS_ATTRIB>
: List<PROPERTY> where PROPERTY : PropertyInfo
The code above defines a list template that will contain objects of type PROPERTY and that type should be a PropertyInfo (or one derived class)
It also defines that for the list to be initialized, a class (System.Type) OBJ_TYPE and a System Attribute PROPERTY_SYS_ATTRIB should be provided.
This way, our list explicitly integrates the Type on which it will operate.
Providing the System Attribute may also allow extending the list template for future use in other contexts.
On list initialization, the template will proceed to listing all properties with the attribute PROPERTY_SYS_ATTRIB defined within the class of OBJ_TYPE:
public bool LoadClassAttributeProperties()
{
Clear();
Type obj_type = typeof(OBJ_TYPE);
// loop through all class's properties.
// insert those properties defined with the specified System.Attribute.
foreach (PropertyInfo p in obj_type.GetProperties())
{
// Does this property define our System.Attribute?. add it to the list.
bool has_sys_attribute = Attribute.IsDefined( p,
typeof( PROPERTY_SYS_ATTRIB), false);
if(has_sys_attribute)
{
PROPERTY new_property = (PROPERTY)p;
Add(new_property);
}
}
return true;
}
The above GetValue and SetValue methods will also have to be slightly modified before being integrated in the list template. Example:
public bool SetValue(OBJ_TYPE obj, string property_name, object value)
{
if( obj == null)
return false;
PROPERTY my_property = this[property_name];
if( my_property == null)
return false;
// is this a readonly property?
if( my_property.CanWrite == false)
return false;
// get the property's data type
Type property_type = my_property.PropertyType;
object new_value;
...
...
Download the sample code ExtensibleObjectProperties.zip (46.35 kb)