Platinum Partner
dotnet,how-to,methodology,microsoft,tools,c#,reflection

Getting Assembly Information Using Reflection

I recently took over a WPF application that needed a little bit of refactoring. One of the places that had a lot of repeated code was the about screen. By using reflection a bunch of properties were being created to display things like the assembly title, version, description and etcetera. The original code looked like this:

public string AssemblyDescription
{
    get
    {
        object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
        if (attributes.Length == 0)
        {
            return "";
        }
        return ((AssemblyDescriptionAttribute)attributes[0]).Description;
    }
}

public string AssemblyProduct
{
    get
    {
        object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
        if (attributes.Length == 0)
        {
            return "";
        }
        return ((AssemblyProductAttribute)attributes[0]).Product;
    }
}

You may have noticed that this code does not follow the DRY principle. In case you arent familiar with the term, DRY stands for Don't repeat yourself. In otherwords, if you repeat the same code multiple times then you should try to refactor it into a more generic method.

After analyzing the code you may notice that both of the property getters do the same thing. First, it gets a handle to the current assembly. Next, the GetCustomAttributes method is called to get an object array which contains all the attribute values for a given type. Finally, a little bit of conditional logic is used to emit a value. Using the power of generics and some reflection we can easily build an extension method that will eliminate the redundant code.

Warning: If you are not familiar with generics, reflection and lambda statements then the following code may cause your brain to explode

public static TAttribute[] GetAttributes<TAttribute>(this Assembly assembly) where TAttribute : Attribute {
    return assembly.GetCustomAttributes(typeof(TAttribute), false) as TAttribute[];
}

public static object GetFirstAttributeValue<TAttribute>(
    this Assembly assembly,
    Expression<Func<TAttribute, object>> propertyLambda) where TAttribute : Attribute {
    Type type = typeof(TAttribute);

    var attributeValues = assembly.GetAttributes<TAttribute>();
    if (attributeValues.Length == 0)
        throw new ArgumentException("No values found");

    var member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda));

    var propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda));

    if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(string.Format("Expresion '{0}' refers to a property that is not from type {1}.", propertyLambda, type));

    var firstValue = attributeValues[0];
    return propInfo.GetValue(firstValue, null);
}

The code above basically takes the lambda statement, makes sure its a property expression and then uses reflection to get the value of the object. By the way, I do not take credit for writing the reflection code, most of it was borrowed from this stackoverflow post. In any case, with the help of the extension method above we can now simplify our code as:

private Assembly Assembly { get; set; }

public AboutViewModel()
{
    Assembly = Assembly.GetExecutingAssembly();
}

public string AssemblyDescription {
    get {
        return Assembly.GetFirstAttributeValue<AssemblyDescriptionAttribute>(x => x.Description).ToString();
    }
}

public string AssemblyProduct {
    get {
        return Assembly.GetFirstAttributeValue<AssemblyProductAttribute>(x => x.Product).ToString();
    }
}

As you can see, the code is much cleaner. Also take notice that I cached the call to Assembly.GetExecutingAssembly() in a private variable. This helps with performance and also cleans up the code a little.

Happy Coding!

Published at DZone with permission of {{ articles[0].authors[0].realName }}, DZone MVB. (source)

Opinions expressed by DZone contributors are their own.

{{ tag }}, {{tag}},

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}
{{ parent.authors[0].realName || parent.author}}

{{ parent.authors[0].tagline || parent.tagline }}

{{ parent.views }} ViewsClicks
Tweet

{{parent.nComments}}