如何确定枚举值的代表类型?

时间:2010-09-21 13:46:36

标签: c# enums casting

考虑以下两个枚举:

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

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

我可以通过显式转换((int)MyEnum1.Value2) == 2((char)MyEnum2.Value2) == 'b'来检索这些枚举值所代表的物理值,但是如果我想在不首先知道类型的情况下获取char表示或int表示,该怎么办?投到?

是否有可能在没有强制转换的情况下获取枚举的基础值,或者至少在程序上可以确定基础值的正确类型?

4 个答案:

答案 0 :(得分:12)

这两个枚举的基础值为int。您只是使用了在第二种情况下从charint的隐式转换这一事实。

例如,如果你看一下Reflector中的第二个枚举,你会看到如下内容:

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

编辑:如果你想要一个不同的基础类型,你必须指定它,例如

public enum Foo : long
{
}

但是,只有bytesbyteshortushortintuintlong和{ {1}}是有效的基础枚举类型。

答案 1 :(得分:8)

所有枚举必须在其声明中使用以下类型之一: bytesbyteshortushortintuintlongulong。这是您指定类型的方式:

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

如果您未指定类型,则默认为int

很遗憾,您无法将char指定为基础类型。您可以将“扩展名”创建为自定义属性:

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

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

然后有一个这样的课程:

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()));
  }
}

(很明显,方法Enum.GetUnderlyingType似乎是你正在寻找的方法,但它永远不会返回char,因为你不能使用该语言的char枚举。)

这将使您能够了解char enums的扩展概念:

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

这会将c打印到控制台。

注意底层类型和char枚举的值,理想情况下它们应该适合char以避免转换失败(溢出)。安全类型为16位宽(与char)或更低:bytesbyteshortushort。如果枚举中的值可以转换为16位字符而不会丢失精度,则其他类型都可以(就像上面的示例一样)。

使用默认的(int)基础类型和char文字作为枚举值(可以隐式转换为int)就足够了。

<强>更新

您可以在F#中声明一个char枚举:

namespace Drawing

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

在C#中,您可以像这样使用它:

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

它会打印System.Char

但是......如果你试图使用它的值,C#会抱怨。这样:

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

出现编译错误:

  

错误CS0570:语言

不支持'Drawing.Color.Red'

在VB.NET中没有编译错误,但Enum.GetName存在运行时错误。似乎运行时没有准备好处理char枚举。这是该方法的摘录(来自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");
  }

它不仅检查类型是枚举,还检查它是上述基础类型之一(char不是一个选项)。 所以你真的不应该在F#中创建char枚举。你可以使用我所描述的“扩展”方法。

答案 2 :(得分:1)

枚举只是幕后的一种int类型。您可以将其更改为long,byte和其他一些,但它们必须是数字。 Char只是在这里工作,因为它可以改为int。所以,对不起,我认为这是不可能的。

答案 3 :(得分:0)

试试这个,对我有用:

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;
}

将该代码添加到扩展类,然后您可以将其用作:

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

当propValue的类型为MyEnum2时,该值将更改为char并为'a','b'或'c'