[英]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.