如何从System.Enum转换为基本整数?


93

我想创建一个通用方法,用于将任何System.Enum派生类型转换为其对应的整数值,而无需强制转换,并且最好不解析字符串。

例如,我想要的是这样的:

// Trivial example, not actually what I'm doing.
class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        (int)anEnum;
    }
}

但这似乎不起作用。Resharper报告您不能将类型为“ System.Enum”的表达式转换为类型为“ int”的表达式。

现在我想出了这个解决方案,但我希望有一些更有效的方法。

class Converter
{
    int ToInteger(System.Enum anEnum)
    {
        return int.Parse(anEnum.ToString("d"));
    }
}

有什么建议?


1
我相信抱怨的是编译器,而不是Resharper。
库格尔2012年

1
不必要。我在System.Enum上有一个扩展方法,Resharper有时会抱怨:无法将实例参数类型'Some.Cool.Type.That.Is.An.Enum'转换为'System.Enum'无疑是一个枚举。如果我编译并运行代码,它就可以正常工作。如果然后我关闭VS,吹走Resharper缓存,然后将其重新启动,则重新扫描后一切都很好。对我来说,这是某种缓存方式。对他来说可能是一样的。
2014年

@Mir我对此也有ReSharper“抱怨”。对我来说也一样。不知道为什么混用这些类型,但是绝对不是编译器。
akousmata

Answers:


134

如果你不想投

Convert.ToInt32()

可以做到的。

(int)enumValue无法直接投放(通过)。请注意,这也将是“危险的”,因为一个枚举可以有不同的基础类型(intlongbyte...)。

更正式地讲:System.Enum与没有直接继承关系Int32(尽管两者都是ValueTypes),因此显式强制转换在类型系统内不能正确


2
Converter.ToInteger(MyEnum.MyEnumConstant);在这里不会给您任何错误。请编辑该部分。
nawfal 2013年

谢谢,@ nawfal,您是对的。我编辑了回复(并学到了新的东西:) ...)
MartinStettner

如果基础类型不同于int
Alex Zhukovskiy

@YaroslavSivakov它不会工作,例如对于long枚举。
Alex Zhukovskiy

System.Enum是一个类,因此它是一个引用类型。值可能已装箱。
Teejay

42

我通过强制转换为对象然后转换为int来使其工作:

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return (int)((object)enumValue);
    }
}

这是丑陋的,可能不是最好的方法。我会不断弄乱它,看看我是否能提出更好的东西。

编辑:刚要发布Convert.ToInt32(enumValue)也可以,并且注意到MartinStettner击败了我。

public static class EnumExtensions
{
    public static int ToInt(this Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

测试:

int x = DayOfWeek.Friday.ToInt();
Console.WriteLine(x); // results in 5 which is int value of Friday

编辑2:在评论中,有人说这仅在C#3.0中有效。我只是像这样在VS2005中测试了它,它的工作原理是:

public static class Helpers
{
    public static int ToInt(Enum enumValue)
    {
        return Convert.ToInt32(enumValue);
    }
}

    static void Main(string[] args)
    {
        Console.WriteLine(Helpers.ToInt(DayOfWeek.Friday));
    }

我给了您+1,但此解决方案仅适用于C#3.0及更高版本。
瓦迪姆

我认为,这只能满足编译器的要求。您是否在运行时进行了测试?我无法将任何值传递给此函数……
MartinStettner's

因为这是扩展方法?还是旧版本C#中的枚举有所不同?
BFree

我在帖子末尾添加了一个测试。我尝试了一下,对我有用。
BFree

@BFree:似乎仅适用于扩展方法。我使用“旧式”函数(在C#2.0中)进行了尝试,但没有找到实际向其传递任何值的语法(这就是我先前的评论所针对的)
MartinStettner,2009年

14

如果您需要将任何枚举转换为其基础类型(并非所有枚举都由来支持int),则可以使用:

return System.Convert.ChangeType(
    enumValue,
    Enum.GetUnderlyingType(enumValue.GetType()));

5
这是唯一的防弹答案。
Rudey

这是造成装箱拆箱吗?
b.ben

@ b.ben是的。Convert.ChangeType接受object第一个参数,然后返回object
德鲁·诺阿克斯

是的,无论如何,我确实同意@Ru​​dey对此进行性能测试。BFree的解决方案是迄今为止最快的。快大约八倍。无论如何,我们仍在谈论壁虱。

10

为什么需要使用辅助方法重新发明轮子?将enum值转换为其基础类型是完全合法的。

使用它的类型更少,在我看来更易读。

int x = (int)DayOfWeek.Tuesday;

而不是像

int y = Converter.ToInteger(DayOfWeek.Tuesday);
// or
int z = DayOfWeek.Tuesday.ToInteger();

6
我想转换任何枚举值。即,我有各种各样的类,它们具有一些枚举字段,这些枚举字段正在由某些代码以通用方式进行处理。这就是为什么将简单类型转换为int不合适的原因。
orj

@orj,我不太确定您的意思。您能否举一个显示所需用法的示例?
路加福音

2
有时,例如在泛型方法的情况下,您无法直接转换枚举。由于不能将泛型方法约束为枚举,因此您已将其约束为诸如struct之类的东西,而该类型不能转换为int形式。在这种情况下,可以使用Convert.ToInt32(enum)来执行此操作。
Daniel T.

我喜欢扩展方法ToInteger()遵循与ToString()相同的格式的想法。我认为一方面想要int值并在需要字符串值时使用ToString()尤其是在代码并排时,一方面将其强制转换为int不太容易。
路易丝·埃格尔顿

同样,使用扩展方法可以增加代码库的一致性。正如@nawfal所指出的那样,既然有多种方法可以从枚举中获取int值,则创建扩展方法并强制使用它意味着您每次获得枚举的int值时都使用相同的方法
路易丝·埃格尔顿

4

从我的回答这里

鉴于e如下所示:

Enum e = Question.Role;

然后这些工作:

int i = Convert.ToInt32(e);
int i = (int)(object)e;
int i = (int)Enum.Parse(e.GetType(), e.ToString());
int i = (int)Enum.ToObject(e.GetType(), e);

最后两个很丑陋。第一个应该更具可读性,尽管第二个要快得多。也许扩展方法是两全其美的。

public static int GetIntValue(this Enum e)
{
    return e.GetValue<int>();
}

public static T GetValue<T>(this Enum e) where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
    return (T)(object)e;
}

现在您可以致电:

e.GetValue<int>(); //or
e.GetIntValue();

您确定第二个更快吗?我认为它将装箱枚举,然后取消装箱回到int吗?
yoyo 2014年

1
@yoyo e变量已经具有实际值类型(即枚举)的框式实例。当您写时,Enum e = Question.Role;它已经装箱了。问题是关于将装箱的盒子转换System.Enum回基本的int类型(拆箱)。因此,此处拆箱仅是性能问题。(int)(object)e是直接的拆箱呼叫;是的,应该比其他方法更快。看到这个
nawfal 2014年

感谢您的解释。我误以为System.Enum的实例是未装箱的值。也请参见-msdn.microsoft.com/zh-cn/library/aa691158(v=vs.71).aspx
yoyo

@yoyo嗯,是的。链接的相关报价:从任何枚举类型到类型System.Enum。
nawfal 2014年

1

从a System.Enum转换int为对我来说效果很好(它也在MSDN上)。也许这是Resharper的错误。


1
您使用什么版本的运行时?
2009年

2
该链接显示的子类型的转换System.Enum,而不是System.Enum其本身。
nawfal

我遇到的一个类似问题是Resharper缓存快照。即使完全重新扫描后,关闭VS并删除Resharper缓存也会使错误消失。
2014年

1

由于枚举仅限于byte,sbyte,short,ushort,int,uint,long和ulong,因此我们可以做一些假设。

我们可以使用最大的可用容器来避免转换期间的异常。不幸的是,使用哪个容器尚不清楚,因为ulong会炸掉负数,而long炸掉long.MaxValue和ulong.MaxValue之间的数字。我们需要根据基础类型在这些选择之间切换。

当然,当结果不适合int时,您仍然需要决定要做什么。我认为投放是可以的,但仍有一些陷阱:

  1. 对于基于字段空间大于int(long和ulong)的类型的枚举,某些枚举可能会得出相同的值。
  2. 如果您处于选中区域,则强制转换大于int.MaxValue的数字将引发异常。

这是我的建议,我将其留给读者来决定在哪里公开此功能。作为助手或扩展。

public int ToInt(Enum e)
{
  unchecked
  {
    if (e.GetTypeCode() == TypeCode.UInt64)
      return (int)Convert.ToUInt64(e);
    else
      return (int)Convert.ToInt64(e);
  }
}

-1

不要忘记Enum类型本身中有一堆静态辅助函数。如果您要做的只是将枚举的实例转换为其对应的整数类型,则强制转换可能是最有效的方法。

我认为ReSharper抱怨是因为Enum不是任何特定类型的枚举,并且枚举本身是从标量值类型而不是Enum派生的。如果您需要以通用方式进行适应性转换,我会说这很适合您(请注意,枚举类型本身也包含在通用中:

public static EnumHelpers
{
    public static T Convert<T, E>(E enumValue)
    {
        return (T)enumValue;
    }
}

然后可以这样使用:

public enum StopLight: int
{
    Red = 1,
    Yellow = 2,
    Green = 3
}

// ...

int myStoplightColor = EnumHelpers.Convert<int, StopLight>(StopLight.Red);

我不能肯定地说,但是C#的类型推断甚至可能支持上面的代码,允许以下操作:

int myStoplightColor = EnumHelpers.Convert<int>(StopLight.Red);

1
不幸的是,在您的示例中E可以是任何类型。泛型不允许我使用Enum作为类型参数约束。我不能说:T:Enum
Vadim

您可以使用where T:struct。它不像您想要的那样严格,但是它将删除任何引用类型(我认为这是您要尝试做的事情。)
jrista

您可以使用:if(!typeof(E).IsEnum)throw new ArgumentException(“ E必须是枚举类型”);
diadiora

1
这甚至是不正确的,静态帮助器甚至无效。
Nicholi 2013年

1
为什么有人应该EnumHelpers.Convert<int, StopLight>(StopLight.Red);代替使用(int)StopLight.Read;?这个问题也在讨论中System.Enum
nawfal 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.