【问题标题】:How to determine the represented type of enum value?如何确定枚举值的表示类型?
【发布时间】:2011-04-15 05:26:06
【问题描述】:

考虑以下两个枚举:

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 表示怎么办?

是否可以在不进行强制转换的情况下获得枚举的基础值,或者至少可以通过编程方式确定基础值的正确类型?

【问题讨论】:

    标签: c# enums casting


    【解决方案1】:

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

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

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

    编辑:如果你想要不同的底层类型,你必须指定它,例如

    public enum Foo : long
    {
    }
    

    但是,只有 bytesbyteshortushortintuintlongulong 是有效的基础枚举类型。

    【讨论】:

    • 这也是我得出的结论。我认为 C# 无论如何都将其视为整数,但我需要有人为我确认这一点。谢谢:)
    • @Nathan:我已经编辑了答案以包含更多内容。基本上你不能有一个底层类型的 char。
    【解决方案2】:

    而 enum 在幕后只是一个 int 类型。您可以将其更改为 long、byte 和其他一些,但它们必须是数字。 Char 只是在这里工作,因为它可以更改为 int。所以,抱歉,我认为这是不可能的。

    【讨论】:

      【解决方案3】:

      所有枚举必须在其声明中使用以下类型之一: 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 枚举的扩展概念:

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

      这会将c 打印到控制台。

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

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

      更新:

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

      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# 中创建字符枚举。你可以使用我描述的“扩展”方法。

      【讨论】:

      • "(顺便说一句,方法 Enum.GetUnderlyingType 似乎是您正在寻找的方法,但它从不返回 char,因为您不能在该语言中使用 char 枚举。)“是的,我对此进行了测试同样,这也是我得出结论的原因,即作为值的类型的 char 实际上是 char 的等效 int32 表示。整个练习甚至不是为了我自己的目的,而是为了确认我在帮助别人时已经得出的结论。感谢您的意见!
      • 很高兴我能帮上忙。您如何看待我提出的“扩展”?它可以帮助你或你的朋友吗?还是只会造成更多问题?
      【解决方案4】:

      试试这个,对我有用:

      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'

      【讨论】:

      • 这并不能告诉您源代码中的枚举是使用 int 还是 char。它只是告诉您枚举 可能 是。这是危险代码。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-18
      相关资源
      最近更新 更多