This is an advanced sequel to C#: Enums and Strings Are Friends. One of particularly interesting features of an enum is the possibility of extending its values with attributes.

public enum Digits
    {
        [Arabic("1")]
        [Roman("I")]
        One=1,
        [Arabic("2")]
        [Roman("II")]
        Two,
        [Arabic("3")]
        [Roman("III")]
        Three
    }
These values can be obtained using .NET reflection mechanism. .NET already defines a lot of useful attributes such as Description, DisplayName, DefaultValue.

On top of that you are able to derive your custom attributes from the Attribute class. By adding properties to this class you can attach a plethora of information to each value of an enum.
[AttributeUsage(AttributeTargets.Field)]
public class RomanAttribute : Attribute
{
    private readonly string _digit;

    public string Digit
    {
        get { return _digit; }
    }

    public RomanAttribute(string title)  // url is a positional parameter
    {
        _digit = title;
    }
}
Wouldn't it be nice if we could read value of any property of any attribute straight off enum value? The problem with this concept is ... properties of different attributes have different names. For example: DescriptionAttribute has property named Description, and DisplayNameAttribute has property named DisplayName.

Luckily we live in the age of generics and reflections. So reading these properties no longer requires hard coded attribute type and target property. You can simply pass attribute type, property type, property name and enum to a function and let reflection do its business.

// Read [Description] attribute.
Enum e = Days.Sat;
string s = e.GetAttributeProperty<DescriptionAttribute, string>("Description");
Console.WriteLine("Description is {0}", s);
// Read [DisplayName ] attribute.
s = e.GetAttributeProperty<DisplayNameAttribute, string>("DisplayName");
Console.WriteLine("Display name is {0}", s);
// Find enum value based on [Description].
Enum ef = e.FindEnumValueByAttributeProperty<DescriptionAttribute, string>("Description","Friday");
All that is left is to write these two conversion functions.
public static class EnumEx
{
    #region Enum Extensions
    public static PT GetAttributeProperty<AT, PT>(this Enum this_, string propertyName)
        where AT : Attribute
        where PT : class
    {
        // First get all attributes of type A.
        AT[] attributes = 
            (this_.GetType().GetField(this_.ToString())).GetCustomAttributes(typeof(AT), false) as AT[];

        if (attributes == null || attributes.Length == 0) // Null or can't cast?
            return null;
        else 
        { // We have something.
            AT a = attributes[0];
            PropertyInfo pi = a.GetType().GetProperty(propertyName);
            if (pi != null)
            {
                PT result = pi.GetValue(a, null) as PT;
                return result;
            }
            else
                return null;
        }
    }

    public static Enum FindEnumValueByAttributeProperty<AT, PT>(this Enum this_, string propertyName, PT propertyValue)
        where AT : Attribute
        where PT : class, IComparable
    {
        // First get all enum values.
        Array enums = Enum.GetValues(this_.GetType());
        foreach (Enum e in enums)
        {
            PT p = e.GetAttributeProperty<AT, PT>(propertyName);

            if (p!=null && p.Equals(propertyValue))
                return e;
        }
        return null;
    }
    #endregion // Enum Extensions
}
UPDATE: It seems like the usage of < and > symbols in code corrupted the listings. Fixed it.

If you need a portable .NET solution for converting RGB to HLS and vice versa there are libraries around to do it. For Windows only using the Shell Lightweight Utility Functions is a simpler alternative.

[DllImport("shlwapi.dll")]
static extern int ColorHLSToRGB(int H, int L, int S);
[DllImport("shlwapi.dll")]
static extern void ColorRGBToHLS(int RGB, ref int H, ref int L, ref int S);
// RGB2HLS
ColorRGBToHLS(ColorTranslator.ToWin32(rgbColor, ref h, ref l, ref s);
// HLS2RGB
Color rgbColor=ColorTranslator.FromWin32(ColorHLSToRGB(h, l, s));

Many thanks to John Boker for his concise explanation. What a time saver.

"Margin is on the outside of block elements while padding is on the inside. Use margin to separate the block from things outside it, padding to move the contents away from the edges of the block."

Newer Posts Older Posts Home

Blogger Syntax Highliter