Taoffi's blog

prisonniers du temps

Xamarin: the missing Description attribute

If you are a Windows C# programmer, you may know about the DescriptionAttribute and its help in describing an object, a property, method or any other member of a class in a human way.

You may also use [Description], to associate a Label to a property and later use it in the UI.

As Xamarin.Forms does not (yet) offer this simple useful attribute, I decided to write one.

The basic code

Our Description attribute class is quite simple:

[AttributeUsage(validOn: AttributeTargets.All, AllowMultiple = false, Inherited = false)]
public class DescriptionAttribute : Attribute
{
    public string _description;

    public DescriptionAttribute(string description)
    {
         _description = description;
    }

    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }
} 

 

With this in place, we can now write things like:

[Description("This is my object")]
public class MyClass
{
    [Description("This is my constructor")]
    public MyClass()
    {
    }

    [Description("My property")]
    public string Property1
    {
     … 
    }

 

 

Reading back the descriptions

Well, but for this to be useful, we must do something to get back these descriptions when needed.

First: how to read the descriptions assigned to objects and members?

Some extension helpers can simplify this work (remember: we are in portable (PCL) library, and also using System.Reflection):

public static string Description(this Type objectType)
{
    if(objectType == null)
        return null;

    TypeInfo typeInfo = objectType.GetTypeInfo();
    var attrib = typeInfo.GetCustomAttribute<DescriptionAttribute>();
    return attrib == null ? null : attrib.Description;
}



To get the description of MyClass, The above extension method now allows us to write:

string    classDescription    = typeof(MyClass).Description();

 

Another method may even make that simpler: get the description of an object's instance of a given class:

public static string Description(this object obj)
{
    if(obj == null)
        return null;

    return obj.GetType().Description();
}


 

This allows us to write: myObject.Description(); to get the description of myObject's class.

Reading back the description of class members

Reading a given member (property / method…) description, we have to look into the MemberInfo object of that given member to find the Description attribute:

 

public static string MemberDescription(this MemberInfo member)
{
    if(member == null)
        return null;

    var attrib = member.GetCustomAttribute<DescriptionAttribute>();
    return attrib == null ? null : attrib.Description;
}

 

 

How to get a member info? Not quite handy!...

Let us simplify a little more. The following code (though it may be somehow difficult to read… see System.Linq.Expressions) will allow a much easier syntax.

 

public static string PropertyDescription<TMember>( Expression<Func<TMember>> memberExpression)
{
    if (memberExpression == null)
        return null;

    var expression = memberExpression.Body as MemberExpression;
    var member = expression == null ? null : expression.Member;

    if (member == null)
        return null;

    return member.MemberDescription();
}

 

 

The above method analyses its Expression parameter to extract the MemberInfo for us and calls the original method to extract the description of the member.

To use this method to extract the description of 'Property1' , we can write:

string propertyLabel = PropertyDescription(()=> Property1);

 

What about Enums?

As you know, we often give short (occasionally cryptic!) names for enum members. Displaying these names in a clear human-readable manner to the user for selecting a value is usually a challenge.

The Description attribute can help us assign these labels. Reading them back is somehow another challenge. The reason is that enum members are fields (in contrast to member properties and methods we saw before)

Actually, with enums, we have two challenges: find the description of each element, but also be able to retrieve the value of a selected description.

For the first challenge, find the description of an element, let us try this extension:

 

public static string EnumDescription(this Enum value)
{
    if(value == null)
        return null;

    var type = value.GetType();
    TypeInfo typeInfo = type.GetTypeInfo();
    string typeName = Enum.GetName(type, value);

    if (string.IsNullOrEmpty(typeName))
        return null;

    var field = typeInfo.DeclaredFields.FirstOrDefault(f => f.Name == typeName);

    if(field == null)
        return typeName;

    var attrib = field.GetCustomAttribute<DescriptionAttribute>();
    return attrib == null ? typeName : attrib.Description;
}

 

 

Sample usage:

 

public enum Flowers
{
    [Description("African lily")]
    Agapanthus,

    [Description("Alpine thistle")]
    Eryngium,

    [Description("Amazon lily")]
    Eucharis,
};

 

string africanLilly = Flowers.Agapanthus.EnumDescription(); //"African lily"
string alpineThistle = Flowers.Eryngium.EnumDescription(); //"Alpine thistle"

 

Find the enum value by its description

The second task is to retrieve the enum value by its description. For instance when the user selects one of the displayed descriptions.

 

public static int EnumValue(this Enum value, string selectedOption)
{
    var values = Enum.GetValues(value.GetType());

    foreach(Enum v in values)
    {
        string description = v.EnumDescription();

        if(description == selectedOption)
            return (int) v;
    }

    return 0; // arbitrary default value
}

 

 

Now we can write:

 

Flowers africanLilly = Flowers.EnumValue("African lily");    // Agapanthus
Flowers amazonLilly = Flowers.EnumValue("Amazon lily");    // Eucharis


 

Convert a List<T> to List<something known only at runtime>

I fall into this while writing a data generator for sample objects.

Let us assume this:

 

public class SampleItem
{
public string name { getset; }
public SampleItem() { }
}

 

public class SampleClass
{
public List<SampleItem> ObjectList {  getset; }
public SampleClass() { }
}

 

Using Reflection, the data generator can easily see and assign a string value to SampleItem.Name property (notice: at runtime, our code does not know what SampleItem is)

For my purpose, it was important to insert some sample items into any List<T> property of an object.

In the above sample code that means, for a SampleClass instance, to generate a list of SampleItem to be assigned to the instance's ObjetcList property.

An elegant solution was found here. Its code is so simple:

 

public static object ConvertList(List<object> value, Type type)
{
var containedType = type.GenericTypeArguments.First();
return value.Select(item => Convert.ChangeType(item, containedType));
}

 

Example usage:

 

var objects = new List<Object> { 1, 2, 3, 4 };
ConvertList(objects, typeof(List<int>)).Dump();

 

That didn't work for my case. I could not assign the ObjectList property (I got an exception) because the resulting value generated by the code above was in fact a Lis<object>. It only converted the items contained into the list into SampleItem, the list itself was not a List<SampleItem> it was still a List<object>

 

The solution (less elegant, but still efficientJ):

 

static object ConvertList(IEnumerable<object> value, Type listType)
{
var containedType = listType.GenericTypeArguments.First();
var tmpList     = value.Select(item => Convert.ChangeType(item, containedType)).ToList();
var newList     = Activator.CreateInstance(listType);
MethodInfo addMethod = listType.GetMethod("Add");

foreach(var item in tmpList)
  addMethod.Invoke(newList, new object[] { item });

return newList;
}

 

That converts a List<object> and returns an object of the type expected by the property (a List<T>)

Object Visibility and learning cycle… just a vision of

In everyday work, we come through new tools or new versions of tools we knew before.

The cycle of playing with and manipulating these tools’ objects to attain a reasonable level of mastering… is often daunting!

To minimize the learning cycle of tools, some helpful features have been introduced: like documentation, tooltips… etc.

Documentation is of course important, but, for me, tooltips are much more helpful. They just appear when I need them in the context of using the object or the property.

The thing is: when you manipulate an object for the first time, you really know few things about its composition and, even less, about its role in the global mechanics of the tool in question. You just know it is there, and it should be there for some reason and that you should spend some time to understand: its role, its structure, and finally how can it be useful for you (if at all it could be!)

One way to shorten this learning cycle is to let others show you how to use the tool and its object. Quite useful, but, on one hand, this occults some side of self-experience (important)… and also negatively interferes in your critical view of the tool (which is often useful for the tool’s enhancement itself)

What we do to know about (most) simple toys seems more rational: You just set something on or off (left/right or up/down…)… then put the toy on work and see... after some cycles you end up by figuring out what is the ‘best’ position for your needs (or mood!).

Another representation, which may also give an interesting slant related to this subject, is the DeepZoom technology used in maps applications where you can first see the whole world map, and then, zooming-in on the map you get more details about a given country, city, streets, buildings… etc.

A good path for reducing the time and effort needed to learn a subject or a tool would be:

  • To be able to see (explore) the global image of the subject or tool’s structure in action;
  • Be able to zoom-in on its objects and see their properties (progressively detailed according to your zoom level);
  • Be able to change the value of a give object’ property… and perceive the impact of the change on the global behavior.

This proposed path cannot of course be applied in all situations or contexts, but can be useful in many (most) cases.

We may, for instance, need to create a simulation context inside which we can ‘run’ the specific tool or object. An approach which may also be useful for product tests and benchmarking.

Some interesting works have been done on some aspects (like The Property Grid project)… More is to be done on the visualization of objects and properties by zoom level. Will try to write a sample on this in a future post.

 

Revealing object properties

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)

 

 

Invoking methods by name. A first approach

As many developers know, one of the interesting namespaces in .NET is System.Reflection.

Here I try to use Reflection to invoke loaded assemblies’ methods by their names. This can be quite useful for applications like storing menu or button actions in a database and bringing them to life during runtime (I will post something about this subject shortly).

The attached test application demonstrates this in a very simple manner. Each dialog button invokes a method defined somewhere in the loaded assembly using its name. The search is done by few methods in MethodFinder static class.

 

The dialog displays 3 buttons. The OnClick handler for each button invokes a method by its name:

private void button1_Click(object sender, EventArgs e)

{

       invoke_method("Method1");

}

 

private void button2_Click(object sender, EventArgs e)

{

       invoke_method("Method2");

}

 

The invoke_method function, calls a method finder to retrieve the method and, if found, invokes it:

 

private void invoke_method(string method_name)

{

       MethodInfo          method       = MethodFinder.FindMethod(method_name);

       string              str_result   = "";

 

       if (method != null)

       {

             str_result = method.Invoke(this, null).ToString();

             MessageBox.Show(str_result);

       }

       else

             MessageBox.Show("Couldn't find method in loaded assemblies");

}

 

What you should know

You probably know that a .NET assembly is composed of modules (generally just one module), each module is composed of classes (Types) and each class is composed of fields, properties and methods.

The MethodFinder.FindMethod function loops through runtime loaded assemblies, modules and classes to search for the method and return the found method if any.

 

FindMethod function:

public static MethodInfo FindMethod( string method_name)

{

       foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())

       {

             method = FindAssemblyMethod( asm, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

FindAssemblyMethod function loops through assembly’s classes:

public static MethodInfo FindAssemblyMethod(Assembly asm, string method_name)

{

       MethodInfo   method;

 

       foreach (Module module in asm.GetModules())

       {

             method = FindModuleMethod( module, method_name);

             if( method != null)

                    return method;

       }

       return null;

}

 

As you have already guessed, FindModuleMethod function loops through module’s classes to search the method:

public static MethodInfo FindModuleMethod(Module module, string method_name)

{

       Type[]       module_types;

       MethodInfo   method;

 

       module_types = module.GetTypes();

 

       foreach (Type module_class in module_types)

       {

             method = FindClassMethod( module_class, method_name);

             if( method != null)

                    return method;

       }

 

       return null;

}

 

Finally FindClassMethod searches for the method inside the class’s available methods:

public static MethodInfo FindClassMethod( Type module_class, string method_name)

{

       MethodInfo[] methods;

 

       methods = module_class.GetMethods();

       foreach (MethodInfo method in methods)

       {

             if( string.Compare( method.Name, method_name, true) == 0)

                    return method;

       }

 

       return null;

}

 

This is a first approach that I believe that many useful applications can be built on.

I will expose some ideas on the subject in a next post.

 

Download the sample code (with some more details inside) InvokeMethodByName.zip (30.31 kb)