4
votes

I have inherited a web api that has lots of enums defined in code, I want to convert them to a view-model class called EnumView so they can be serialized as below...

{Id: value, Name: enumName}

public class EnumView
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Upon restricting the Generic class to the enum type, I get the warning

Constraint cannot be special class 'System.Enum'

This is the generic converter that I was going to use...

public class EnumViewConverter<T> where T : Enum
{
    public static List<EnumView> ConvertToView()
    {
        List<EnumView> enumViews = new List<EnumView>();

        T[] enumValues = (T[])Enum.GetValues(typeof(T));

        foreach (var enumValue in enumValues)
        {
            var enumView = new EnumView
            {
                Id = (int)enumValue,
                Name = Enum.GetName(typeof(T), enumValue)
            };

            enumViews.Add(enumView);
        }
        return enumViews;
    }
}

Without T being constrained to an enum, the following conversion doesn't compile...

Id = (int)enumValue,

Due to the issue around a lack of generic enum constraints, what's the best way to go about this?

3
Ok, you have a defined enum, but how are you going to use it? by answering this question, we find out the purpose of the conversion, then we can possibly come up with a solutionHamed
@leppie - I had read that question/answer before posting but don't feel it really answers my question.Will
@Hamed not sure quite what you're after - the EnumView is to be serialized in the API. The enum is used in serverside code...Will
actually (int)enumValue doesn't compile I guess.Because there's no guarantee that enumValue must be an int. BTW, Enum's are serializable too.Amit Kumar Ghosh
@AmitKumarGhosh Yep, type T isn't guaranteed an enum if I use where T: struct for example. I want to create a list of all the values of the enum and return this in the API, not just serialize a single enum value.Will

3 Answers

3
votes

You could use : Id = Convert.ToInt32(enumValue) instead of the casting (int)

And if you want to add some 'constraint' to check the type at the compilation you could set 'where T : struct' it will at least prevent to set class type.

0
votes

In the end I used...

public static class EnumViewConverter<T> where T : struct
{
    public static List<EnumView> ConvertToView()
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");

        List<EnumView> enumViews = new List<EnumView>();

        T[] enumValues = (T[])Enum.GetValues(typeof(T));

        foreach (var enumValue in enumValues)
        {
            var enumView = new EnumView
            {
                Id = Convert.ToInt32(enumValue),
                Name = Enum.GetName(typeof(T), enumValue)
            };

            enumViews.Add(enumView);
        }
        return enumViews;
    }
}

Called by...

var views = EnumViewConverter<FooBarEnum>.ConvertToView();

Thanks for all the help, could have sworn I tried this earlier:(

0
votes

To expand on my earlier comment, the technique described in this answer uses a nested class with a generic parameter dependent on its parent class to emulate generic Enum constraints. You could use this approach here:

public abstract class ConverterClassUtils<TClass> 
    where TClass : class
{
    public class ViewConverter<TInner> where TInner : struct, TClass
    {
        public static List<EnumView> ConvertToView()
        {
            List<EnumView> enumViews = new List<EnumView>();

            TInner[] enumValues = (TInner[])Enum.GetValues(typeof(TInner));

            foreach (var enumValue in enumValues)
            {
                var enumView = new EnumView
                {
                    Id = (int)(object)enumValue,
                    Name = Enum.GetName(typeof(TInner), enumValue)
                };

                enumViews.Add(enumView);
            }
            return enumViews;
        }
    }
}

public class EnumConverter : ConverterClassUtils<Enum> { }

then the following compiles:

var view = EnumConverter.ViewConverter<SomeEnum>.ConvertToView();

while this does not:

var view = EnumConverter.ViewConverter<int>.ConvertToView();