题 在C#中将int转换为枚举


怎么可以 int 被投到一个 enum 在C#?


2571
2017-08-27 03:58


起源


换句话说: 得到-INT-值从 - 枚举 - nawfal
免费5分钟视频,涵盖以下最佳答案: app.deviq.com/courses/how-do-i-convert-int-to-enum-in-c-sharp - ssmith


答案:


从字符串:

YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);
// the foo.ToString().Contains(",") check is necessary for enumerations marked with an [Flags] attribute
if (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))
  throw new InvalidOperationException($"{yourString} is not an underlying value of the YourEnum enumeration.")

来自int:

YourEnum foo = (YourEnum)yourInt;

更新:

从数字你也可以

YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum) , yourInt);

3123
2017-08-27 03:59



@FlySwat,如果 YourEnum 是动态的,只会在运行时知道,我想要的是转换为 Enum? - Shimmy
请注意,如果您的代码被混淆,Enum.Parse将无法工作。在混淆后的运行时,将字符串与枚举名称进行比较,此时枚举的名称不是您期望的那样。因此,您的解析将在成功之前失败。 - jropella
谨防 如果您使用上面的“from a string”语法并传入一个无效的字符串(例如“2342342” - 假设它不是您的枚举值),它实际上会允许它而不会抛出错误!你的枚举将具有该值(2342342),即使它不是枚举本身的有效选择。 - JoeCool
我觉得这个答案现在有点过时了。对于字符串,你应该真正使用 var result = Enum.TryParse(yourString, out yourEnum) 现在(并检查结果以确定转换是否失败)。 - Justin T Conroy
它也有可能 Enum.Parse 通过添加一个不区分大小写 true 调用的参数值: YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString, true); - Erik Schierboom


只是施展它:

MyEnum e = (MyEnum)3;

你可以检查它是否在范围内使用 Enum.IsDefined

if (Enum.IsDefined(typeof(MyEnum), 3)) { ... }

721
2017-08-27 04:01



请注意,如果使用Flags属性,则不能使用Enum.IsDefined,并且值是标志的组合,例如:Keys.L | Keys.Control - dtroy
关于 Enum.IsDefined,请注意它可能很危险: msdn.microsoft.com/en-us/library/ms229025(VS.90).aspx - adrian
另一方面,如果我根据库提供的枚举验证输入,在将其发送到库本身之前... - Alexander
我更喜欢这个定义: “返回指示指定枚举中是否存在具有指定值的常量的指示” 从 MSDN - Pap
...因为你的定义可能会产生误导,因为你说: “......检查它是否在范围内......” 这意味着在一系列具有开始和结束限制的数字中...... - Pap


或者,使用扩展方法而不是单行:

public static T ToEnum<T>(this string enumString)
{
    return (T) Enum.Parse(typeof (T), enumString);
}

用法:

Color colorEnum = "Red".ToEnum<Color>();

要么

string color = "Red";
var colorEnum = color.ToEnum<Color>();

198
2017-11-11 13:27



为了处理用户输入,调用Enum.Parse的重载可能是一个好主意,它允许你指定比较不区分大小写(即用户键入“red”(小写)会导致上述代码崩溃而不进行此更改。) - BrainSlugs83
很酷,除了它不是问题。 - nawfal
方便,但问题特别询问有关整数。 - BJury
如果字符串是整数,例如,这也适用。 “2” - TruthOf42
如果enumString为null,则会抛出异常(昨天有类似的问题)。考虑使用TryParse而不是Parse。 TryParse还将检查T是否为枚举类型 - Justin


我想要得到一个完整的答案,人们必须知道enums如何在.NET内部工作。

事情怎么样

.NET中的枚举是一组将一组值(字段)映射到基本类型的结构(默认为 int)。但是,您实际上可以选择枚举映射到的整数类型:

public enum Foo : short

在这种情况下,枚举被映射到 short 数据类型,这意味着它将作为简短存储在内存中,并在您投射和使用它时表现为短。

如果从IL的角度来看它,(normal,int)枚举如下所示:

.class public auto ansi serializable sealed BarFlag extends System.Enum
{
    .custom instance void System.FlagsAttribute::.ctor()
    .custom instance void ComVisibleAttribute::.ctor(bool) = { bool(true) }

    .field public static literal valuetype BarFlag AllFlags = int32(0x3fff)
    .field public static literal valuetype BarFlag Foo1 = int32(1)
    .field public static literal valuetype BarFlag Foo2 = int32(0x2000)

    // and so on for all flags or enum values

    .field public specialname rtspecialname int32 value__
}

这里应该引起你注意的是 value__ 与枚举值分开存储。在枚举的情况下 Foo 以上,类型 value__ 是int16。这基本上意味着你可以在枚举中存储你想要的任何东西, 只要类型匹配

在这一点上,我想指出这一点 System.Enum 是一种值类型,基本上就是这个意思 BarFlag 将占用内存中的4个字节 Foo 将占用2 - 例如基础类型的大小(实际上比这更复杂,但是嘿......)。

答案

因此,如果你有一个想要映射到枚举的整数,那么运行时只需做两件事:复制4个字节并将其命名为其他东西(枚举的名称)。复制是隐式的,因为数据存储为值类型 - 这基本上意味着如果使用非托管代码,则可以简单地交换枚举和整数而无需复制数据。

为了安全起见,我认为这是最好的做法 知道底层类型是相同的或隐式可转换的 并确保存在枚举值(默认情况下不会检查它们!)。

要查看其工作原理,请尝试以下代码:

public enum MyEnum : int
{
    Foo = 1,
    Bar = 2,
    Mek = 5
}

static void Main(string[] args)
{
    var e1 = (MyEnum)5;
    var e2 = (MyEnum)6;

    Console.WriteLine("{0} {1}", e1, e2);
    Console.ReadLine();
}

请注意,转换为 e2 也有效!从上面的编译器角度来看,这是有道理的: value__ 字段只是填充5或6和何时 Console.WriteLine 电话 ToString(),的名字 e1 是在解决的同时解决了 e2 不是。

如果这不是您的意图,请使用 Enum.IsDefined(typeof(MyEnum), 6) 检查您要投射的值是否映射到定义的枚举。

另请注意,即使编译器实际检查了这一点,我也明确了枚举的基础类型。我这样做是为了确保我不会遇到任何意外。要查看这些惊喜,您可以使用以下代码(实际上我已经看到这在数据库代码中发生了很多):

public enum MyEnum : short
{
    Mek = 5
}

static void Main(string[] args)
{
    var e1 = (MyEnum)32769; // will not compile, out of bounds for a short

    object o = 5;
    var e2 = (MyEnum)o;     // will throw at runtime, because o is of type int

    Console.WriteLine("{0} {1}", e1, e2);
    Console.ReadLine();
}

116
2018-04-03 07:39



我意识到这是一个老帖子,但是你如何在c#中获得这种知识水平?这是通过阅读C#规范吗? - Rolan
@Rolan我有时希望更多的人会这么想。 :-)说实话,我真的不知道;我试着了解事情是如何运作的,并且只要我能得到它就能得到信息。我确实读过C#标准,但我也经常用Reflector反编译代码(我甚至经常查看x86汇编程序代码)并做了大量的小实验。此外,在这种情况下,了解其他语言也有帮助;我已经做了大约30年的CS,并且在某些时候某些事情变得“符合逻辑” - f.ex.一个枚举应该是整数类型,因为否则互操作会破坏(或者你的表现会消失)。 - atlaste
我认为正确进行软件工程的关键是了解工作原理。对我而言,这意味着如果您编写一段代码,您就知道它大致转换为f.ex.处理器操作和内存提取/写入。如果你问如何达到这个水平,我建议建立一大堆小测试用例,让它们变得更加艰难,每次尝试预测结果,然后测试它们(包括反编译等)。在弄清楚所有细节和所有特征后,您可以检查(无聊)标准是否正确。至少,这将是我的方法。 - atlaste
很棒的回答,谢谢!在上一个代码示例中,它在运行时抛出异常,因为o是一个对象。只要它在短程范围内,就可以将int变量强制转换为short。 - gravidThoughts
@gravidThoughts谢谢。实际上它是一个拆箱操作,所以它不会像你描述的那样进行任何隐式转换。如果你不知道细节,有时会在C#中混淆......无论如何,因为 int != short,它会抛出(拆箱失败)。如果你这样做 object o = (short)5;,它会工作,因为那时类型将匹配。这不是关于范围,而是关于类型。 - atlaste


采用以下示例:

int one = 1;
MyEnum e = (MyEnum)one;

89
2017-08-27 04:00





我正在使用这段代码将int转换为我的枚举:

if (typeof(YourEnum).IsEnumDefined(valueToCast)) return (YourEnum)valueToCast;
else { //handle it here, if its not defined }

我觉得这是最好的解决方案。


54
2017-10-21 10:05



不适用于标志枚举 - Daniel Fisher lennybacon
这很好。我很惊讶在将无效值转换为int支持的枚举时没有异常。 - orion elenzil
这实际上与最受欢迎的答案没有什么不同。该答案还讨论了在将字符串转换为Enum类型后使用Enum.IsDefined。因此,即使字符串没有错误地转换,Enum.IsDefined仍将捕获它 - mmcrae


以下是Enums的一个很好的实用程序类

public static class EnumHelper
{
    public static int[] ToIntArray<T>(T[] value)
    {
        int[] result = new int[value.Length];
        for (int i = 0; i < value.Length; i++)
            result[i] = Convert.ToInt32(value[i]);
        return result;
    }

    public static T[] FromIntArray<T>(int[] value) 
    {
        T[] result = new T[value.Length];
        for (int i = 0; i < value.Length; i++)
            result[i] = (T)Enum.ToObject(typeof(T),value[i]);
        return result;
    }


    internal static T Parse<T>(string value, T defaultValue)
    {
        if (Enum.IsDefined(typeof(T), value))
            return (T) Enum.Parse(typeof (T), value);

        int num;
        if(int.TryParse(value,out num))
        {
            if (Enum.IsDefined(typeof(T), num))
                return (T)Enum.ToObject(typeof(T), num);
        }

        return defaultValue;
    }
}

44
2017-09-07 04:42





如果你准备好了4.0 。净 框架,有一个新的 Enum.TryParse() 功能非常有用并且与[Flags]属性配合得很好。看到 Enum.TryParse方法(String,TEnum%)


37
2017-11-01 14:58



从字符串转换时这很有用。但是从int转换时却没有。 - CodesInChaos