通过反射获取枚举值

55

我有一个简单的枚举类型

 public enum TestEnum
 {
     TestOne = 3,
     TestTwo = 4
 }

var testing = TestEnum.TestOne;

我希望通过反射获取它的值(3)。有什么想法吗?


针对您的评论:在任何意义上您“拥有”值TestEnum.TestOne的情况下,您已经拥有3。那么您实际上拥有什么? - AakashM
我在这里看到太多的答案都指出 (int)TestEnum。如果不是必要的话,一个人不会问如何使用反射来完成它。也许是依赖注入或者我来这里的原因是标准库与框架特性的兼容性。如果有的话。 - Courtney The coder
16个回答

73

非常好的问题Mat。

问题的情景如下:

你有一些未知的枚举类型和该类型的一些未知值,你想获取该未知值的底层数字值

这是使用反射进行的一行代码方式:

object underlyingValue = Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()));

如果值恰好是TestEnum.TestTwo,那么value.GetType()将等于typeof(TestEnum)Enum.GetUnderlyingType(value.GetType())将等于typeof(int),而值将是3(装箱;有关装箱和拆箱值的更多详细信息,请参见http://msdn.microsoft.com/en-us/library/yz2be5wk.aspx)。
为什么有人需要编写这样的代码?在我的情况下,我有一个例程,将视图模型中的值复制到模型中。我在所有ASP.NET MVC项目的处理程序中使用它作为非常干净和优雅的架构的一部分,以编写不具有Microsoft模板生成的处理程序所具有的安全问题的处理程序。
这个模型是由 Entity Framework 从数据库生成的,其中包含一个 int 类型的字段。视图模型具有某种枚举类型的字段,我们称之为 RecordStatus,我已在项目中定义了它。我决定在我的框架中完全支持枚举。但是现在,模型中的字段类型与视图模型中对应字段的类型不匹配。我的代码会检测到这一点,并使用类似于上面给出的一行代码将枚举转换为 int。

6
你的回答很好,回答了你自己提出的问题,但我认为它并不是对此处所问问题的回答。 - user743382
7
谢谢您的评论,实际上这马特提出的问题的答案。 - Miltos Kokkonidis
5
具体地说,Mat 问如何使用反射获取某个简单枚举类型(TestEnum)的枚举值(testing)的(基础)值(3)。Daniel 和 Fredrik 认为他问错了,回答说不需要使用反射,而 Pranay 回答了一个完全不同的问题(如何使用反射列出枚举类型的值)。正是因为目前得到赞同的答案没有回答所提出的问题,所以我对问题进行了重新表述、回答,并给出了一个可能需要执行 Mat 所问的操作的示例!:-) - Miltos Kokkonidis
2
@hvd:感谢您的评论。我想说,传递给Convert.ChangeType的第二个参数是一个类型对象(即System.Type类型的对象,它是System.Reflection.MemberInfo的子类),这一事实本身就意味着即使转换本身使用了.NET CLR的反射基础设施!因此,我认为我没有忽略Mat问题中的要求,他在首次提出这个问题时也没有错!如果没有这个要求,那么Mat似乎是在寻找Fredrik和Daniel所给出的答案。 - Miltos Kokkonidis
3
我回答了问题,没有试图猜测或怀疑Mat表达自己想要的能力。如果如你所说,他想要从"TestEnum.TestOne"到3而不是使用反射从TestEnum.TestOne到3,那么我同意他的问题没有表达清楚。但就目前来看,这是一个完全合理的问题,现在已经有了答案。 :-) @mjmcloug:有任何评论吗? - Miltos Kokkonidis
显示剩余5条评论

45

您可以使用System.Enum帮助程序:

System.Type enumType = typeof(TestEnum);
System.Type enumUnderlyingType = System.Enum.GetUnderlyingType(enumType);
System.Array enumValues = System.Enum.GetValues(enumType);

for (int i=0; i < enumValues.Length; i++)
{
    // Retrieve the value of the ith enum item.
    object value = enumValues.GetValue(i);

    // Convert the value to its underlying type (int, byte, long, ...)
    object underlyingValue = System.Convert.ChangeType(value, enumUnderlyingType);

    System.Console.WriteLine(underlyingValue);
}

输出

3
4


7
这是更好的答案。因为我之前也问了同样的问题,但我发现这是 XY 问题。反思并不是我需要的。 - Bitterblue
1
当将值转换为 int (.Net 4.5) 时,我会收到此错误“类型为 'System.InvalidCastException' 的未处理异常”。 - Clay Lenhart
4
并非所有的枚举类型都是 int!枚举类型还可以是 sbyte、byte、short、ushort、uint、long、ulong 和 char。你应该将其强制转换为底层类型,正如所接受的答案建议的那样。 - Anders Marzi Tornblad
的确,我已经相应地更新了我的答案(此时,Miltos的答案也涵盖了这一点,但我仍然会让我的答案更加准确)。 - OLP
1
这就是我在寻找的答案!它对我有用。谢谢 :) - Najeeb
我喜欢有更多的代码,而不仅仅是必要的简单英语。 - ziya

26

完整的代码: 如何在 C# 中使用反射获取枚举值

MemberInfo[] memberInfos = 
        typeof(MyEnum).GetMembers(BindingFlags.Public | BindingFlags.Static);
string alerta = "";
for (int i = 0; i < memberInfos.Length; i++) {
    alerta += memberInfos[i].Name + " - ";
    alerta += memberInfos[i].GetType().Name + "\n";
}

15
我没有给出负分,但我猜即使答案是正确的,它并不是非常具有描述性。这里有一个链接和一些代码,自己去理解吧。可以通过几种方式来改进它,其中之一是提供示例输出。 - John McDonald

6

如果您在反射上下文中加载了一个程序集,则GetValue和GetValues方法不起作用。但是我们可以采用另一种方法:

var val = typeof(MyEnum).GetFields()
    .Where(fi => fi.IsLiteral && fi.Name == "TestOne")
    .Select(fi => fi.GetRawConstantValue())
    .First();

3

为什么需要使用反射?

int value = (int)TestEnum.TestOne;

7
;) 是的,我知道如何用这种方式做。但我需要使用反射。 - mat-mcloughlin
4
因为我们不知道TestEnum的类型,我们只知道我们有一个枚举。 - Clay Lenhart
如果我没有这个信息 namespace.TestEnum,只有这个 "namespace.TestEnum",该怎么办? - T.S.
@T.S. 你可以使用 Type.GetType(yourString) 来获取 System.Type - Daniel A. White
@T.S. 这个回答/问题已经有8年了。当我最初发布它时,关于为什么需要反射的上下文并不多。这是使用KISS原则。 - Daniel A. White
显示剩余3条评论

3

根据您的要求,这很简单,就像其他人已经指出的那样。只需将枚举对象转换为整数,您就可以获得枚举的数字值。

int value = (int) TestEnum.TestOne;

然而,如果需要将枚举值与 | (按位或)混合使用,例如:
var value = TestEnum.TestOne | TestEnum.TestTwo;

如果您希望获取混合值所代表的选项,以下是您可以执行的操作(注意:这适用于由int值表示且旨在利用位运算的枚举):
首先,在字典中获取枚举选项及其值。
var all_options_dic = typeof(TestEnum).GetEnumValues().Cast<object>().ToDictionary(k=>k.ToString(), v=>(int) v);

将字典过滤,仅返回混合选项。

var filtered = all_options_dic.Where(x => (x.Value & (int) options) != 0).ToDictionary(k=>k.Key, v=>v.Value);

使用您的选项执行任何逻辑,例如打印它们、将它们转换为列表等等。:

foreach (var key in filtered.Keys)
        {
            Console.WriteLine(key + " = " + filtered[key]);
        }

希望这能帮到你。

2
尝试以下内容:
System.Array enumValues = System.Enum.GetValues(typeof(MyEnum));
Type underlyingType = System.Enum.GetUnderlyingType(MyEnum);

foreach (object enumValue in enumValues)
    System.Console.WriteLine(String.Format("{0}",Convert.ChangeType(enumValue ,underlyingType)));

1
使用此方法通过反射获取枚举整数值
protected static object PropertyValue(object obj, string propertyName)
{
  var property = obj.GetType().GetProperty(propertyName);
  if(property == null)
    throw new Exception($"{propertyName} not found on {obj.GetType().FullName}");

  if (property.PropertyType.IsEnum)
    return (int) property.GetValue(obj);
  return property.GetValue(obj);
}

1
你可以使用Gethashkey函数来获取未知类型的枚举值。
Enum.Parse(enumType, enumvalue).GetHashCode();

更多细节
获取枚举类型动态地
  private static Type GetEnumType(string enumName)
    {
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                var type = assembly.GetType(enumName);
                if (type == null)
                    continue;
                if (type.IsEnum)
                    return type;
            }
        return null;
    }

阅读枚举值。
Enum.GetValues(enumType);

我希望它能帮助到某个人!

(保留HTML)

0

或者,如果你需要实际的枚举对象(类型为TestEnum):

MemberInfo[] memberInfos = typeof(MyEnum).GetMembers(BindingFlags.Public | BindingFlags.Static);
string alerta = "";
for (int i = 0; i < memberInfos.Length; i++) {

alerta += memberInfos[i].Name + " - ";


/* alerta += memberInfos[i].GetType().Name + "\n"; */ 

// the actual enum object (of type MyEnum, above code is of type System.Reflection.RuntimeFieldInfo)
object enumValue = memberInfos[i].GetValue(0);
alerta += enumValue.ToString() + "\n";
}

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