4

I know generics are done at compiled time, but I am getting confused with the way generics work (and I though I knew generics..).

I have created the following extension method:

public static class EnumExt
{
    /// <summary>
    /// Gets the description, if any, or the name of the enum as a string in a enum type
    /// </summary>
    public static string GetDescription<T>(this T enumType) where T : struct, IConvertible
    {
        FieldInfo fieldInfo = enumType.GetType().GetField(enumType.ToString());
        DescriptionAttribute[] descriptionAttributes = (DescriptionAttribute[])
            fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (descriptionAttributes.Length > 0)
        {
            return descriptionAttributes[0].Description;
        }
        return enumType.ToString();
    }
}

I have as an example the following enum

namespace MyProject.Model
{
    [Flags]
    public enum MyEnumType
    {
        [Description("None")]
        None = 0,
        [Description("Show Products (default)")]
        Products  = 1,
        [Description("Show Tariffs")]
        Tariffs = 2
    }
}

And now I'd like to use it from an HttpHelper in MVC that returns a string (html text) like this one. Please note My class has access to my EnumExt methods.

    public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper)
    {
        if (!typeof(TModel).IsEnum)
        {
            throw new ArgumentException("this helper can only be used with enums");
        }
        TModel[] allEnumValues = (TModel[])Enum.GetValues(typeof(TModel));
        foreach (TModel item in allEnumValues)
        {
            var descErr = item.GetDescription(); //does not compile, but I know it's a MyEnumType.Tariffs..
            var descOk = MyEnumType.Tariffs.GetDescription(); //this line works
            //descOk = "Show Tariffs"
        }

        return new HtmlString("ideally this is some html checkboxes with each enum description");
    }

I know I can get all the Enum values and iterate through them using the TModel like this:

TModel[] allEnumValues = (TModel[])Enum.GetValues(typeof(TModel));

but if I know a TModel is an enum (it's a MyEnumType), why can't I use it to access the enum extension method like:

allValues[0].GetDescription<>(); //ERROR. this does not compile

I guess it's because somehow I must cast it to a specific type like MyEnumType, but how to do so and keep it generic?

Thanks in advance!

UPDATE: Thanks to the first answers I was able to compile by restricting TModel to struct, IConvertible

diegosasw
  • 13,734
  • 16
  • 95
  • 159
  • 1
    Seems like `TModel` should be `struct, IConvertble` too – tukaef Aug 05 '15 at 03:22
  • Is it possible that by restricting ```TModel``` to ```struct, IConvertible``` and being unable to make it an Enum I cannot use then the GetDescription enum extension method because it's not a "real" enum anymore? I am able to compile but ```item.GetDescription()``` does not return "Show Tariffs" but "Tariffs" – diegosasw Aug 05 '15 at 03:44

3 Answers3

3

Because your extension method is defined for T where T : struct, IConvertible.

But TModel in CheckBoxesForEnumModel doesn't conform to these generic type constraints.

You should change the signature from

public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper)

to

public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper)
    where TModel : struct, IConvertible

or more restrictive.

Nick Hill
  • 4,867
  • 3
  • 23
  • 29
  • I replied to nawfal with this answer that also applies to your solution: Thank you. With your changes it compiles, however the T in the extension method seems to have descriptionAttributes == 0 so it's not working as expected hence it's returning the string 'Tariffs' instead 'Show Tariffs' as per its Description Attribute. I'm still missing something :( – diegosasw Aug 05 '15 at 03:40
  • Duh.. I was running a version without the Description. Thank you and sorry. It's all good as you say. – diegosasw Aug 05 '15 at 04:08
2

You need a constraint for your method. Do,

public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper) where TModel : struct, IConvertible
{
   if (!typeof(TModel).IsEnum)
   {
      throw new ArgumentException("this helper can only be used with enums");
   }

   //Here some code to get all the values and the names for this Enum        
   //But HOW?? 

   return new HtmlString("ideally this is some html checkboxes with each enum description");
}

I hope the reason is obvious.

D J
  • 6,908
  • 13
  • 43
  • 75
nawfal
  • 70,104
  • 56
  • 326
  • 368
1

You created an extension method for your enum based on T where T is struct an implements IConvertible.

But in your HtmlHelper extension method your TModel does not have the same constraints, so there is not way that compiler can associate your enum extension method based on a type that is a struct and IConvertible with your TModel which is just a type.

Adding the same constraints to your HtmlHelper method will do the trick:

public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper) where TModel : struct, IConvertible
{
    string description = htmlHelper.ViewData.Model.GetDescription();
}
IPValverde
  • 2,019
  • 2
  • 21
  • 38