Convert.ChangeType和枚举类型转换?

70

我从数据库中获得了一个 Int16 值,并且需要将其转换为枚举类型。不幸的是,这是在一个代码层中完成的,该层仅通过反射了解对象的非常少的信息。

因此,它最终调用了 Convert.ChangeType,但会出现无效的强制转换异常。

我找到了一个我认为有点不妥的解决方法,像这样:

String name = Enum.GetName(destinationType, value);
Object enumValue = Enum.Parse(destinationType, name, false);

有没有更好的方法,这样我就不必进行这个字符串操作了?

以下是一份简洁但完整的程序,如果有人需要进行实验,可以使用它:

using System;

public class MyClass
{
    public enum DummyEnum
    {
        Value0,
        Value1
    }

    public static void Main()
    {
        Int16 value = 1;
        Type destinationType = typeof(DummyEnum);

        String name = Enum.GetName(destinationType, value);
        Object enumValue = Enum.Parse(destinationType, name, false);

        Console.WriteLine("" + value + " = " + enumValue);
    }
}

1
哎呀...我需要在喝咖啡之前停止尝试回答这样的问题... - Daniel Schaffer
我现在明白了,Console.WriteLine也在一个无法访问枚举类型的层中。我完全误解了。删除了我的(愚蠢的)答案。 - GvS
3个回答

92

Enum.ToObject(.... 就是你要找的东西!

C#

StringComparison enumValue = (StringComparison)Enum.ToObject(typeof(StringComparison), 5);

VB.NET

Dim enumValue As StringComparison = CType([Enum].ToObject(GetType(StringComparison), 5), StringComparison)

如果你经常进行枚举转换,请尝试使用下面的类,它将帮助你节省大量的代码。

public class Enum<EnumType> where EnumType : struct, IConvertible
{

    /// <summary>
    /// Retrieves an array of the values of the constants in a specified enumeration.
    /// </summary>
    /// <returns></returns>
    /// <remarks></remarks>
    public static EnumType[] GetValues()
    {
        return (EnumType[])Enum.GetValues(typeof(EnumType));
    }

    /// <summary>
    /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    /// <remarks></remarks>
    public static EnumType Parse(string name)
    {
        return (EnumType)Enum.Parse(typeof(EnumType), name);
    }

    /// <summary>
    /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
    /// </summary>
    /// <param name="name"></param>
    /// <param name="ignoreCase"></param>
    /// <returns></returns>
    /// <remarks></remarks>
    public static EnumType Parse(string name, bool ignoreCase)
    {
        return (EnumType)Enum.Parse(typeof(EnumType), name, ignoreCase);
    }

    /// <summary>
    /// Converts the specified object with an integer value to an enumeration member.
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    /// <remarks></remarks>
    public static EnumType ToObject(object value)
    {
        return (EnumType)Enum.ToObject(typeof(EnumType), value);
    }
}

现在,您可以直接编写Enum<StringComparison>.ToObject(5);,而不是编写(StringComparison)Enum.ToObject(typeof(StringComparison), 5);


4
传奇!我之前不知道这个。StackOverflow太棒了,不是吗? :-) - Doctor Jones
苦苦挣扎了一个多小时,终于通过这个解决了问题!即使是在发布两年后,也应该要给予一个真诚的点赞 ;) - Edurne Pascual
1
ToObject() 似乎允许枚举中不存在的值,以及超出基础类型范围的值:Enum.ToObject(typeof(IntEnumType), (long)Int32.MaxValue + 1) - Nelson Rothermel
1
@NelsonRothermel:C#本身允许枚举值不在枚举中定义,因此这并不奇怪。与强制转换相比,您的另一个观点更准确,但似乎只是第一个观点的特例(它似乎首先将其转换为基础类型?) - Michael Bray
我在 IntelliSense 中看到了这个方法很多次,但对它的任务一无所知。设计师本可以给这个方法起一个更直观的名称。 - Zoltán Tamási
现在您可以使用C# 7.3中的public class Enum<EnumType> where EnumType : Enum来进一步固定泛型类型。 - Tim

0
如果您正在将枚举存储在 DataTable 中,但不知道哪一列是枚举,哪一列是字符串/整数,您可以通过以下方式访问该值:
foreach (DataRow dataRow in myDataTable.Rows)
{
    Trace.WriteLine("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=");
    foreach (DataColumn dataCol in myDataTable.Columns)
    {
        object v = dataRow[dataCol];
        Type t = dataCol.DataType;
        bool e = false;
        if (t.IsEnum) e = true;

        Trace.WriteLine((dataCol.ColumnName + ":").PadRight(30) +
            (e ? Enum.ToObject(t, v) : v));
    }
}

0
基于 @Peter 在这里的回答,以下是将 Nullable<int> 转换为 Enum 的方法:
public static class EnumUtils
{
        public static bool TryParse<TEnum>(int? value, out TEnum result)
            where TEnum: struct, IConvertible
        {
            if(!value.HasValue || !Enum.IsDefined(typeof(TEnum), value)){
                result = default(TEnum);
                return false;
            }
            result = (TEnum)Enum.ToObject(typeof(TEnum), value);
            return true;
        }
}

使用EnumUtils.TryParse<YourEnumType>(someNumber, out result)在许多情况下都非常有用。例如,在Asp.NET的WebApi控制器中,没有默认保护来防止无效的枚举参数。即使某些人传递了null-1000500000"garbage string"或完全忽略了参数,Asp.NET也会使用default(YourEnumType)值。此外,ModelState在所有这些情况下都将是有效的,因此解决方案之一是使用带有自定义检查的int?类型。

public class MyApiController: Controller
{
    [HttpGet]
    public IActionResult Get(int? myEnumParam){    
        MyEnumType myEnumParamParsed;
        if(!EnumUtils.TryParse<MyEnumType>(myEnumParam, out myEnumParamParsed)){
            return BadRequest($"Error: parameter '{nameof(myEnumParam)}' is not specified or incorrect");
        }      

        return this.Get(washingServiceTypeParsed);            
    }
    private IActionResult Get(MyEnumType myEnumParam){ 
       // here we can guarantee that myEnumParam is valid
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接