繁体   English   中英

使用以编程方式生成的枚举时,无法正确创建通用CustomAttributeData

[英]Unable to create Generic CustomAttributeData properly when using programatically generated Enum

我正在尝试使用动态生成的对象来提供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);

只要有问题的枚举是硬编码的,这就很好用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);        
}

编辑2

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

编辑3

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

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

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

我相信我可以通过使用不同的动态程序集来使其工作。 让我知道这是否适合您:

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

我只是想出了解决此问题的简单方法。 您需要设置当前域的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);
}

这将使您动态生成的枚举起作用

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM