How to determine the represented type of enum value?

為{幸葍}努か 提交于 2019-12-18 17:31:13

问题


Consider the following two enums:

enum MyEnum1 {
    Value1 = 1,
    Value2 = 2,
    Value3 = 3
}

enum MyEnum2 {
     Value1 = 'a',
     Value2 = 'b',
     Value3 = 'c'
}

I can retrieve the physical value represented by these enum values through explicit casting, ((int)MyEnum1.Value2) == 2 or ((char)MyEnum2.Value2) == 'b', but what if I want to get the char representation or the int representation without first knowing the type to cast to?

Is it possible to get the underlying value of an enum without a cast or is it at least programatically possible to determine the correct type of the underlying value?


回答1:


The underlying value of both of those enums is int. You're just using the fact that there's an implicit conversion from char to int in the second case.

For example, if you look at the second enum in Reflector, you'll see something like this:

internal enum MyEnum2
{
    Value1 = 0x61,
    Value2 = 0x62,
    Value3 = 0x63
}

EDIT: If you want a different underlying type, you've got to specify it, e.g.

public enum Foo : long
{
}

However, only byte, sbyte, short, ushort, int, uint, long, and ulong are valid underlying enum types.




回答2:


All enums must use one of the following types in their declaration: byte, sbyte, short, ushort, int, uint, long or ulong. This is how you specify a type:

enum MyEnum3 : long {
  Value1 = 5L,
  Value2 = 9L,
  Value3 = long.MaxValue
}

If you don't specify a type, the default is int.

Unfortunately you can't specify char as an underlying type. You could create that "extension" as a custom attribute:

[AttributeUsage(AttributeTargets.Enum)]
public class CharAttribute : Attribute { }

[Char] enum MyEnum2 {
  Value1 = 'a',
  Value2 = 'b',
  Value3 = 'c'
}

And then have a class like this:

public static class EnumEx {
  public static Type GetUnderlyingType(Type enumType) {
    if (!enumType.IsEnum) throw new ArgumentException();
    if (enumType.GetCustomAttributes(typeof(CharAttribute), false).Length > 0) {
      return typeof(char);
    }
    return Enum.GetUnderlyingType(enumType);
  }
  public static object ConvertToUnderlyingType(object enumValue) {
    return Convert.ChangeType(enumValue,
      GetUnderlyingType(enumValue.GetType()));
  }
}

(Incidently, the method Enum.GetUnderlyingType seems to be the one you were looking for, but it never returns char because you can't have char enums in the language.)

This will allow you to get to your extended notion of char enums:

var value3 = EnumEx.ConvertToUnderlyingType(MyEnum2.Value3);
Console.WriteLine(value3);

This will print c to the console.

Pay attention to the underlying type and the values of your char enums, they should ideally fit into a char to avoid conversion failures (overflows). The safe types are 16-bit wide (just like char) or less: byte, sbyte, short or ushort. Other types are OK if the values in the enum can be converted to 16-bit chars without loss of precision (just like in the example above).

Using the default (int) underlying type and char literals as the enum values (which are implicitly convertible to int) is good enough.

UPDATE:

You can declare a char enum in F#:

namespace Drawing

type Color =
   | Red = 'r'
   | Green = 'g'
   | Blue = 'b'

In C#, you can use it like this:

Console.WriteLine(Enum.GetUnderlyingType(typeof(Color)));

And it will print System.Char.

But ... C# will complain if you try to use its values. This:

Console.WriteLine(Color.Red.ToString());

Gives a compiler error:

error CS0570: 'Drawing.Color.Red' is not supported by the language

In VB.NET there's no compilation error, but there's a runtime error from Enum.GetName. It seems that the runtime is not prepared to deal with char enums. This is an excerpt from that method (from Reflector):

if (((!type.IsEnum && (type != intType)) && ((type != typeof(short)) && (type != typeof(ushort)))) && (((type != typeof(byte)) && (type != typeof(sbyte))) && (((type != typeof(uint)) && (type != typeof(long))) && (type != typeof(ulong)))))
  {
    throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");
  }

It checks not only that the type is an enum, but that it also is one of the aforementioned underlying types (for which char is not an option). So you really shouldn't create char enums in F#. You could use the "extension" approach I've described.




回答3:


And enum is just an int type behind the scenes. You can change it to long, byte and some others, but they have to be numbers. Char just works here because that can just be changed to an int. So, sorry, I don't think this is possible.




回答4:


Try this, works for me:

public static bool IsCharEnum(this Type T)
{
    var enumType = T.IsNullableEnum() ? Nullable.GetUnderlyingType(T) : T;
    if (!enumType.IsEnum) return false;
    try
    {
        return ((int[])Enum.GetValues(enumType)).All(i => char.IsLetter((char)i));
    }
    catch
    {
        return false;
    }
}

public static bool IsNullableEnum(this Type T)
{
    var u = Nullable.GetUnderlyingType(T);
    return (u != null) && u.IsEnum;
}

Add that code to an extensions class then you can use it as:

if (propValue.GetType().IsCharEnum())
{
    propValue = ((char)Convert.ToInt32(propValue)).ToString();
}

When propValue is of type MyEnum2, the value will be changed to a char and be 'a','b', or 'c'



来源:https://stackoverflow.com/questions/3760933/how-to-determine-the-represented-type-of-enum-value

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!