【问题标题】:Get and save enum using reflection使用反射获取和保存枚举
【发布时间】:2015-08-16 16:18:50
【问题描述】:

我有一个项目,其中有一些实现抽象类的程序集。

每个程序集都有一个名为 ResultEnum 的公共枚举。 这个 ResultEnum 的值以 int 形式存储在数据库中。

我有另一个显示一些信息的 web 项目,我希望它也显示这个 int 的字符串表示形式 - ResultEnum 中相应值的名称。

我想要做的是,使用 MEF,加载所有相关程序集(这里没问题),使用反射搜索这个枚举(这里也没有问题),然后以某种方式存储枚举,并将其缓存在为了避免所有这些过程,因为我的 db 表中有数千条记录,因此下次我想将数据库中的 int 转换为字符串表示形式(如有必要,还可以反过来)。

AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(path));
_container = new CompositionContainer(catalog);

try
{
    _container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
    Console.WriteLine(compositionException.ToString());
}

foreach (var task in myTasks)
{
    TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;

    MemberInfo[] infos = instance.GetType().GetMembers(BindingFlags.Public | BindingFlags.Static);
    foreach (MemberInfo member in infos.Where(x => x.Name.Equals("ResultEnum")))
    {
        Console.WriteLine(member);                    
    }
}

你建议下一步应该怎么做? 我应该如何存储/缓存它?

谢谢

【问题讨论】:

  • 我想到了一个从 int 到 string 的字典,不是吗?
  • 我同意,但问题是我如何迭代枚举数的值和名称。而且,有没有比字典更好的解决方案
  • 您是否经常需要执行这些类型的操作?如果是这样,您可能需要考虑使用类似子类枚举或类似技术的东西。我在tyreejackson.com/subclassable-enums 写过关于可分类枚举的文章。例如,我使用 StringIntegerEnum 子类型来处理每个枚举的双重基础值之间的映射。
  • @TyreeJackson 这个解决方案似乎很有趣,我确实需要一个“抽象”枚举器。我会再次阅读并考虑实施此更改,谢谢:)
  • @Alonzzo2 酷。然后我会发布一个关于 Subclassable 枚举的答案。

标签: c# reflection system.reflection


【解决方案1】:

除了@Thomas 的回答:

由于使用反射,您可以从属性中获取精确的 int 值,该具体值的名称可以使用下一个表达式获得:

var enumValueName = Enum.GetName(member.GetType(), member.GetValue(instance));

UPD

我真的很想念您反映 MemberInfos。要应用我的解决方案,您可以通过这种方式更新您的反射:

foreach (var task in myTasks)
{
    TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;

// Reflect properties, not all members
    PropertyInfo[] infos = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Static);
    foreach (PropertyInfo prop in infos.Where(x => x.Name.Equals("ResultEnum")))
    {
        var enumValueName = Enum.GetName(prop.GetType(), prop.GetValue(instance));                  
    }
}

或者您可以将 MemberInfo 转换为 PropertyInfo。

【讨论】:

  • member.GetType() 将返回MemberInfo 的子类,而不是成员值的类型。
  • @ThomasLevesque 是对的,当我在监视窗口中输入这个时:“member.GetValue(instance)”我得到这个:“member.GetValue(instance)'System.Reflection.MemberInfo' 没有包含'GetValue'的定义,并且找不到接受'System.Reflection.MemberInfo'类型的第一个参数的扩展方法'GetValue'(您是否缺少使用指令或程序集引用?)“这意味着它与MemberInfo有关
【解决方案2】:

解决此问题的一种方法是考虑使用可子类枚举技术(有时也称为多态枚举)。

我专门编写了几个泛型类来支持这些类型,您可以阅读有关here 的信息。此外,proposal 已提交给 Github 上的 Roslyn 编译器团队,以向 C# 添加对这些类型的枚举的支持。

下面是一组可子类枚举的示例,这些枚举具有两种基础类型,字符串和整数,使用我项目中的类:

public sealed class Status : StringIntegerEnum<Status>
{
    public static readonly Status Active = new Status("active", 1);
    public static readonly Status Inactive = new Status("inactive", 0);

    private Status(string status, int statusCode) : base(status, statusCode) {}
}

请注意,字符串值与常量名称本身相同,这允许您拥有包含违反 C# 中正常命名约定的字符的底层字符串值。

StringIntegerEnum&lt;tStringIntegerEnum&gt; 基类提供了.AllValues.AllNaturalValues.AllStringValues 静态方法,您可以使用这些方法来枚举枚举值列表或其基础值的两种类型。

【讨论】:

    【解决方案3】:

    来自您的评论:

    我同意,但问题是我如何迭代枚举器的值和名称

    我假设您的意思是“枚举”,而不是“枚举数”。您可以使用Enum.GetValues 方法:

    var valuesToNames =
        Enum.GetValues(enumType)
            .Cast<object>()
            .ToDictionary(o => (int)o, o => Enum.GetName(enumType, o));
    

    还有,有没有比字典更好的解决方案

    如何更好?我认为字典是一个很好的解决方案;你有什么理由想要别的东西吗?

    【讨论】:

    • " 我假设您的意思是"枚举",而不是"枚举数"。" 你是对的。但是我的问题是从您的示例中获取 enumType ,因为我不知道真正的 ResultEnum 类型 - 我使用反射来获取代表它的成员。如何在使用反射时获得枚举的真实类型?希望你能理解我
    • @Alonzzo2,member.PropertyType 呢? (如果是属性;如果是字段则使用 FieldType,如果是方法则使用 ReturnType 等)
    • “MemberInfo 不包含 PropertyType 的定义。”,使用“观察”窗口
    • @Alonzzo2,您需要将其转换为其实际类型(PropertyInfo、FieldInfo、MethodInfo 等)。 MemberInfo 只是这些类型的基类。
    【解决方案4】:

    这就是我最终编写代码的方式:

            resultEnumsForTasks = new Dictionary<string, Dictionary<UInt16, string>>();
            foreach (var task in myTasks)
            {
                Dictionary<UInt16, string> _enum =  new Dictionary<UInt16,string>();
                TaskAbstract instance = (TaskAbstract)task.CreateExport().Value;
                MemberInfo resultEnum = instance.GetType().GetMember("ResultEnum").FirstOrDefault();
                if (resultEnum == null)
                    continue;
    
                string[] names = Enum.GetNames(resultEnum as Type);
                IList<int> vals = (IList<int>)Enum.GetValues(resultEnum as Type);
                for (int i = 0; i < names.Length; i++)
                {
                    _enum.Add(Convert.ToUInt16(vals[i]), names[i]);
                }
                resultEnumsForTasks.Add(instance.GetType().Name, _enum);
            }
    

    这与@n.turakulov 的解决方案非常相似,但是他的解决方案对我不起作用,因为由于某种原因我得到了一个空的 PropertyInfo 列表...

    感谢所有提供帮助的人!

    【讨论】:

      猜你喜欢
      • 2011-06-12
      • 2023-03-31
      • 1970-01-01
      • 2017-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多