4

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?

Will
  • 773
  • 1
  • 7
  • 24
  • 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 solution – Hamed Aug 25 '15 at 11:26
  • @leppie - I had read that question/answer before posting but don't feel it really answers my question. – Will Aug 25 '15 at 11:29
  • @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 Aug 25 '15 at 11:30
  • 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 Aug 25 '15 at 11:31
  • @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 Aug 25 '15 at 11:34
  • @Will: As noted in the question, there is a way you can emulate this (see http://stackoverflow.com/a/28527552/15541) If it is not a duplicate, explain why the solutions do not work for you. – leppie Aug 25 '15 at 11:34
  • @leppie so after testing enumView.Id = (int)EnumUtils.Parse(enumView.Name); I still have the same issue... The type 'T' cannot be used as type parameter 'TEnum' in the generic type or method 'EnumClassUtils.Parse(string)'. There is no boxing conversion or type parameter conversion from 'T' to 'System.Enum'. – Will Aug 25 '15 at 11:55
  • @Will: It seems your actual problem is casting to `int` from an unconstraint type. Would `Id = (int)(object)enumValue;` not be a solution for you? – leppie Aug 25 '15 at 12:09
  • @leppie unfortunately I don't think so - EnumUtils.Parse(enumView.Name); even without the cast to int I get The type 'T' cannot be used as type parameter 'TEnum' in the generic type or method 'EnumClassUtils.Parse(string)'. There is no boxing conversion or type parameter conversion from 'T' to 'System.Enum'. – Will Aug 25 '15 at 12:53
  • @leppie thanks for your suggestions so far - the answer suggested as a duplicate allows converting a string back to a well know concrete type of enum but not using a generic in it's place i.e. the calling EnumUtils.Parse("enumValue") – Will Aug 25 '15 at 12:58
  • You will have to duplicate the nested class pattern found in that answer instead of trying to call `EnumUtils.Parse` directly i.e. define a `ViewConverter` and then define `EnumViewConverter : ViewConverter`. The class nesting is used to emulate the constraint on `T` so you can't call it from other generic code. – Lee Aug 25 '15 at 13:11
  • @Lee sorry not quite keeping up with you...please can you post a bit more detail? – Will Aug 25 '15 at 13:39

3 Answers3

3

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.

Mickael Thumerel
  • 516
  • 4
  • 14
  • thanks, I thought I had tested this already and it hadn't worked; anyhow, tried again and you're spot on. – Will Aug 25 '15 at 13:57
0

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:(

Will
  • 773
  • 1
  • 7
  • 24
0

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();
Community
  • 1
  • 1
Lee
  • 142,018
  • 20
  • 234
  • 287
  • thanks for expanding on your suggestion, in the end I went with the code below as it was simpler. Thanks for your help. – Will Aug 27 '15 at 10:20