使用以编程方式生成的枚举

时间:2015-11-20 03:20:41

标签: c# .net reflection enums custom-attributes

我正在尝试使用动态生成的对象来源PropertyGrid。

对于此属性网格上的组合选择,我构建了一个TypeConverter(其中T是枚举,定义了选项列表):

    public class TypedConverter<T> : StringConverter where T : struct, IConvertible
    {
        ...

        public override System.ComponentModel.TypeConverter.StandardValuesCollection
            GetStandardValues(ITypeDescriptorContext context)
        {
            if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");            

            string[] values = Enum.GetValues(typeof(T)).OfType<object>().Select(o => o.ToString()).ToArray();

            return new StandardValuesCollection(values);
        }

    }

然后我可以在属性中添加一个自定义属性,引用此TypeConverter,如下所示(typedConverterGenericType是具有枚举泛型参数的TypedConverter的类型)

CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType });

propertyBuilder.SetCustomAttribute(attributeBuilder);

只要有问题的Enum是硬编码的,就可以使用很棒AddTypeConverterAttribute(propertyBuilder, typeof(TypedConverter<Fred>));。在调试器中,属性上的属性为我提供了{[System.ComponentModel.TypeConverterAttribute( ...

但是,当我使用动态构建的枚举(我已确定在反射中正确生成)不起作用时:

   Type enumType = enumBuilder.CreateType();//This generates a proper enum, as I have determined in reflection

   Type converterType = typeof(TypedConverter<>);

   Type typedConverterType = converterType.MakeGenericType(enumType);

   AddTypeConverterAttribute(propertyBuilder, typedConverterType);

在调试器中,属性上的属性现在给我{System.Reflection.CustomAttributeData},并且在此处钻取,我在ConstructorArguments ... Mscorlib_CollectionDebugView<System.Reflection.CustomAttributeData>(type.GetProperties()[1].CustomAttributes).Items[4].ConstructorArguments' threw an exception of type 'System.IO.FileNotFoundException'上有错误

我做错了什么?如何正确设置TypeConverter属性?

编辑:如果有人想看我如何添加属性

private void AddTypeConverterAttribute(PropertyBuilder propertyBuilder, Type typedConverterGenericType)
{
   CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType });

   propertyBuilder.SetCustomAttribute(attributeBuilder);        
}

EDIT2

测试确认它是动态构建的枚举的问题 - 如果我使用Type typedConverterType = converterType.MakeGenericType(typeof(Fred));创建泛型类型,它可以正常工作。

编辑3

我的测试项目可用here。它正在从Resouces中读取一些JSON,并尝试生成一个类,该类的类型由该JSON描述。

我正在创建一个将要获取PropertyGrid的类(Activator.CreateInstance)的实例。为了在该PropertyGrid上获得组合选择,我创建了一个Type,其属性归属于TypedConverter,其中T是一个枚举,用于描述组合选择中的值。

这适用于硬编码的枚举,但不适用于以编程方式生成的枚举

2 个答案:

答案 0 :(得分:0)

我相信我能够通过使用不同的动态组件来实现这一点。如果这对你有用,请告诉我:

AppDomain currentDomain = AppDomain.CurrentDomain;
AssemblyName enumAssembly = new AssemblyName("enumAssembly");

AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
    enumAssembly, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule(enumAssembly.Name, 
    enumAssembly.Name + ".dll");

// Define a public enumeration with the name "Foo" and an 
// underlying type of Integer.
EnumBuilder eb = mb.DefineEnum("Foo", TypeAttributes.Public, typeof(int));

eb.DefineLiteral("Bar", 0);
eb.DefineLiteral("Baz", 1);

Type final_foo = eb.CreateType();

ab.Save(enumAssembly.Name + ".dll");
var converterType = typeof(TypedConverter<>);

AssemblyName dynamicAsm = new AssemblyName();
dynamicAsm.Name = "DynamicAsm";

// To generate a persistable assembly, specify AssemblyBuilderAccess.RunAndSave.
AssemblyBuilder myAsmBuilder = currentDomain.DefineDynamicAssembly(dynamicAsm,
                                                AssemblyBuilderAccess.RunAndSave);
// Generate a persistable single-module assembly.
ModuleBuilder myModBuilder =
    myAsmBuilder.DefineDynamicModule(dynamicAsm.Name, dynamicAsm.Name + ".dll");

TypeBuilder myTypeBuilder = myModBuilder.DefineType("CustomerData",
                                                TypeAttributes.Public);

PropertyBuilder custNamePropBldr = myTypeBuilder.DefineProperty("elevation",
                                                PropertyAttributes.HasDefault,
                                                final_foo,
                                                null);


var typedConverterType = converterType.MakeGenericType(final_foo);

CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
    typeof(TypeConverterAttribute).GetConstructor(
        new Type[] { typeof(Type) }), 
        new Type[] { typedConverterType }
    );

custNamePropBldr.SetCustomAttribute(attributeBuilder);

答案 1 :(得分:0)

我刚才发现这个问题有一个简单的解决方案。您需要设置当前域的AssemblyResolve事件,并在事件处理程序中返回请求的程序集:

AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{

    return AppDomain
               .CurrentDomain
               .GetAssemblies()
               .FirstOrDefault(assembly => assembly.FullName == args.Name);
}

这将使动态生成的枚举工作