如何在C#中将int转换为枚举?


当前回答

以下是稍微好一点的扩展方法:

public static string ToEnumString<TEnum>(this int enumValue)
{
    var enumString = enumValue.ToString();
    if (Enum.IsDefined(typeof(TEnum), enumValue))
    {
        enumString = ((TEnum) Enum.ToObject(typeof (TEnum), enumValue)).ToString();
    }
    return enumString;
}

其他回答

在C#中将int转换为enum的简单明了的方法:

public class Program
{
    public enum Color : int
    {
        Blue   = 0,
        Black  = 1,
        Green  = 2,
        Gray   = 3,
        Yellow = 4
    }

    public static void Main(string[] args)
    {
        // From string
        Console.WriteLine((Color) Enum.Parse(typeof(Color), "Green"));

        // From int
        Console.WriteLine((Color)2);

        // From number you can also
        Console.WriteLine((Color)Enum.ToObject(typeof(Color), 2));
    }
}

我更喜欢使用可为null的枚举类型变量。

var enumValue = (MyEnum?)enumInt;

if (!enumValue.HasValue)
{
    throw new ArgumentException(nameof(enumValue));
}

这将使用Tawani的实用程序类中的泛型将整数或字符串解析为.NET 4.0中部分匹配的目标枚举。我使用它来转换命令行开关变量,这可能是不完整的。由于枚举不能为空,因此应在逻辑上提供默认值。可以这样称呼:

var result = EnumParser<MyEnum>.Parse(valueToParse, MyEnum.FirstValue);

代码如下:

using System;

public class EnumParser<T> where T : struct
{
    public static T Parse(int toParse, T defaultVal)
    {
        return Parse(toParse + "", defaultVal);
    }
    public static T Parse(string toParse, T defaultVal)
    {
        T enumVal = defaultVal;
        if (defaultVal is Enum && !String.IsNullOrEmpty(toParse))
        {
            int index;
            if (int.TryParse(toParse, out index))
            {
                Enum.TryParse(index + "", out enumVal);
            }
            else
            {
                if (!Enum.TryParse<T>(toParse + "", true, out enumVal))
                {
                    MatchPartialName(toParse, ref enumVal);
                }
            }
        }
        return enumVal;
    }

    public static void MatchPartialName(string toParse, ref T enumVal)
    {
        foreach (string member in enumVal.GetType().GetEnumNames())
        {
            if (member.ToLower().Contains(toParse.ToLower()))
            {
                if (Enum.TryParse<T>(member + "", out enumVal))
                {
                    break;
                }
            }
        }
    }
}

仅供参考:问题是关于整数,没有人提到整数也会在Enum.TryParse()中显式转换

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

工作原理

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

public enum Foo : 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();
}

如果您已经准备好使用4.0.NETFramework,那么有一个新的Enum.TryParse()函数非常有用,并且可以很好地使用[Flags]属性。请参阅Enum.TryParse方法(字符串,TEnum%)