如何在C#中将int转换为枚举?
当前回答
这将使用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()中显式转换
其他回答
或者,使用扩展方法而不是单行:
public static T ToEnum<T>(this string enumString)
{
return (T) Enum.Parse(typeof (T), enumString);
}
用法:
Color colorEnum = "Red".ToEnum<Color>();
OR
string color = "Red";
var colorEnum = color.ToEnum<Color>();
我需要两个指示:
YourEnum possibleEnum = (YourEnum)value; // There isn't any guarantee that it is part of the enum
if (Enum.IsDefined(typeof(YourEnum), possibleEnum))
{
// Value exists in YourEnum
}
从int:
YourEnum foo = (YourEnum)yourInt;
从字符串:
YourEnum foo = (YourEnum) Enum.Parse(typeof(YourEnum), yourString);
// The foo.ToString().Contains(",") check is necessary for
// enumerations marked with a [Flags] attribute.
if (!Enum.IsDefined(typeof(YourEnum), foo) && !foo.ToString().Contains(","))
{
throw new InvalidOperationException(
$"{yourString} is not an underlying value of the YourEnum enumeration."
);
}
从一个数字:
YourEnum foo = (YourEnum)Enum.ToObject(typeof(YourEnum), yourInt);
在我的例子中,我需要从WCF服务返回枚举。我还需要一个友好的名称,而不仅仅是enum.ToString()。
这是我的WCF类。
[DataContract]
public class EnumMember
{
[DataMember]
public string Description { get; set; }
[DataMember]
public int Value { get; set; }
public static List<EnumMember> ConvertToList<T>()
{
Type type = typeof(T);
if (!type.IsEnum)
{
throw new ArgumentException("T must be of type enumeration.");
}
var members = new List<EnumMember>();
foreach (string item in System.Enum.GetNames(type))
{
var enumType = System.Enum.Parse(type, item);
members.Add(
new EnumMember() { Description = enumType.GetDescriptionValue(), Value = ((IConvertible)enumType).ToInt32(null) });
}
return members;
}
}
下面是从Enum获取Description的Extension方法。
public static string GetDescriptionValue<T>(this T source)
{
FieldInfo fileInfo = source.GetType().GetField(source.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fileInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return source.ToString();
}
}
实施:
return EnumMember.ConvertToList<YourType>();
我认为要得到一个完整的答案,人们必须知道枚举在.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();
}