使用反射

时间:2015-08-16 16:18:50

标签: c# reflection system.reflection

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

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

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

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

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

您认为下一步应该是什么? 我该如何存储/缓存它?

由于

4 个答案:

答案 0 :(得分: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。

答案 1 :(得分:1)

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

我写了几个泛型类专门用来支持这些类型,你可以阅读here。此外,已向Github上的Roslyn编译器团队提交了proposal,以便为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<tStringIntegerEnum>基类提供.AllValues.AllNaturalValues.AllStringValues个静态方法,您可以使用这些方法枚举枚举值列表或其基础值的两种类型。

答案 2 :(得分:0)

来自你的评论:

  

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

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

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

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

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

答案 3 :(得分:0)

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

        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的空列表...

感谢所有协助的人!