如何将 int 枚举为枚举?

如何将int enum为 C#中的enum

答案

从字符串:

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

只需投放:

MyEnum e = (MyEnum)3;

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

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

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

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

我认为要获得完整的答案,人们必须知道. NET 内部枚举的工作方式。

事情怎么样

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

public enum Foo : short

在这种情况下,枚举被映射为short数据类型,这意味着它将以 short 形式存储在内存中,并且在您进行转换和使用时表现为 short。

如果从 IL 的角度来看它,则(普通,整数)枚举看起来像这样:

.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__与枚举值分开存储。对于上面的枚举Foovalue__的类型为 int16。这基本上意味着只要类型匹配 ,您就可以在枚举中存储任何内容。

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

答案

因此,如果您有要映射到枚举的整数,则运行时只需执行 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__字段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();
}

请看以下示例:

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

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

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

我发现它是最好的解决方案。

下面是一个不错的 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;
    }
}

对于数字值,这样做更安全,因为无论如何,它将返回一个对象:

public static class EnumEx
{
    static public bool TryConvert<T>(int value, out T result)
    {
        result = default(T);
        bool success = Enum.IsDefined(typeof(T), value);
        if (success)
        {
            result = (T)Enum.ToObject(typeof(T), value);
        }
        return success;
    }
}

如果您准备好使用 4.0 .NET Framework,则有一个新的Enum.TryParse()函数,该函数非常有用,并且可以与 [Flags] 属性配合使用。请参见Enum.TryParse 方法(字符串,TEnum%)

如果您有一个用作位掩码的整数,并且可以表示 [Flags] 枚举中的一个或多个值,则可以使用以下代码将各个标志值解析为一个列表:

for (var flagIterator = 0; flagIterator < 32; flagIterator++)
{
    // Determine the bit value (1,2,4,...,Int32.MinValue)
    int bitValue = 1 << flagIterator;

    // Check to see if the current flag exists in the bit mask
    if ((intValue & bitValue) != 0)
    {
        // If the current flag exists in the enumeration, then we can add that value to the list
        // if the enumeration has that flag defined
        if (Enum.IsDefined(typeof(MyEnum), bitValue))
            Console.WriteLine((MyEnum)bitValue);
    }
}

请注意,这假设enum的基础类型是带符号的 32 位整数。如果它是不同的数字类型,则必须更改硬编码 32 以反映该类型中的位(或使用Enum.GetUnderlyingType()编程方式派生它)