Generics, enums and custom attributes - is it possible?

c#, generics

Solution

Try this code:

public static class EnumListHelper
{
    private static void EnsureIsEnum<TEnum>()
    {
        if (!typeof(TEnum).IsEnum)
            throw new InvalidOperationException(string.Format("The {0} type is not an enum.", typeof(TEnum)));
    }

    public static List<TEnum> FromDatabase<TEnum>(string aDisplayModeString)
        where TEnum : struct
    {
        EnsureIsEnum<TEnum>();
        //Default behaviour
        List<TEnum> result = new List<TEnum>();

        //Split the string list into a list of strings
        List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));

        //Iterate the enum looking for matches in the list
        foreach (Enum displayModeEnum in Enum.GetValues(typeof(TEnum)))
        {
            if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
            {
                result.Add((TEnum)(object)displayModeEnum);
            }
        }

        return result;
    }

    public static string ToDatabase<TEnum>(List<TEnum> aDisplayModeList)
        where TEnum : struct
    {
        EnsureIsEnum<TEnum>();
        string result = string.Empty;

        foreach (var listItem in aDisplayModeList.OfType<Enum>())
        {
            if (result != string.Empty)
                result += ",";
            result += listItem.GetCustomUserData();
        }

        return result;
    }
}

var fromDatabase = EnumListHelper.FromDatabase<TestEnum>("test");
EnumListHelper.ToDatabase(fromDatabase);

UPDATE 0

To be clear, because we cannot restrict generics to Enum we should check that the type `TEnum` is an enum and throw an exception if it is not. When we use the `FromDatabase` method we know that `TEnum` is enum, and we can write this code to cast an enum to the specified `TEnum`:

result.Add((TEnum)(object)displayModeEnum)

in the `ToDatabase` method we also know that `TEnum` is enum and we can write this code to convert `TEnum` to the `Enum` type:

aDisplayModeList.OfType<Enum>()

Problem

Apologies for the amount of code, but it is easier to explain it this way. I have a custom attribute CustomUserData implemented as follows: ``` public class CustomUserData : Attribute { public CustomUserData(object aUserData) { UserData = aUserData; } public object UserData { get; set; } } ``` and an extension method for enums as: ``` public static class EnumExtensions { public static TAttribute GetAttribute<TAttribute>(this Enum aValue) where TAttribute : Attribute { Type type = aValue.GetType(); string name = Enum.GetName(type, aValue); return type.GetField(name) .GetCustomAttributes(false) .OfType<TAttribute>() .SingleOrDefault(); } public static object GetCustomUserData(this Enum aValue) { CustomUserData userValue = GetAttribute<CustomUserData>(aValue); return userValue != null ? userValue.UserData : null; } } ``` I then have a helper class that serializes/deserializes an enum that has custom data associated with it as follows: ``` public static class ParameterDisplayModeEnumListHelper { public static List<ParameterDisplayModeEnum> FromDatabase(string aDisplayModeString) { //Default behaviour List<ParameterDisplayModeEnum> result = new List<ParameterDisplayModeEnum>(); //Split the string list into a list of strings List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(',')); //Iterate the enum looking for matches in the list foreach (ParameterDisplayModeEnum displayModeEnum in Enum.GetValues(typeof (ParameterDisplayModeEnum))) { if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0) { result.Add(displayModeEnum); } } return result; } public static string ToDatabase(List<ParameterDisplayModeEnum> aDisplayModeList) { string result = string.Empty; foreach (ParameterDisplayModeEnum listItem in aDisplayModeList) { if (result != string.Empty) result += ","; result += listItem.GetCustomUserData(); } return result; } } ``` however this is specific to ParameterDisplayModeEnum and I have a bunch of enums that I need to treat this way for serialization/deserialization so I would prefer to have a generic such as: ``` public static class EnumListHelper<TEnum> { public static List<TEnum> FromDatabase(string aDisplayModeString) { //Default behaviour List<TEnum> result = new List<TEnum>(); //Split the string list into a list of strings List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(',')); //Iterate the enum looking for matches in the list foreach (TEnum displayModeEnum in Enum.GetValues(typeof (TEnum))) { if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0) { result.Add(displayModeEnum); } } return result; } public static string ToDatabase(List<TEnum> aDisplayModeList) { string result = string.Empty; foreach (TEnum listItem in aDisplayModeList) { if (result != string.Empty) result += ","; result += listItem.GetCustomUserData(); } return result; } } ``` However this will not work as GetCustomUserData() cannot be invoked. Any suggestions? I cannot change the use of the custom attribute or the use of the enums. I am looking for a generic way to do the serialization/deserialization without having to write a concrete list helper class each time. All suggestions appreciated.

Original source