我使用Mono.Cecil自动生成(许多简单,通用)工厂方法,为库提供方便的API。为标记有特殊自定义属性的属性生成工厂。要生成它们,我必须知道这种属性的类型。非一般情况很简单:
ModuleDefinition module = /* obtained from ReadAssembly */
foreach (var type in module.Types)
if (/* type is marked with the right attribute */)
foreach (var prop in type.Properties)
if (/* prop is marked with the right attribute */)
GenerateFactory(type, prop, prop.PropertyType);
然而,标记的某些类型实际上是泛型。在这种情况下,类型上的属性包含应为其创建工厂的泛型参数,如下所示:
[EmitFactories(typeof(int))]
public class Class<T>
{
[MagicProperty]
T Property { get; set; }
}
(这里,我希望为Class<int>.Property
制作工厂)。
我通过type
一个GenericInstanceType
处理此案例。但是,我无法获取属性的类型 - 枚举type.Properties
我需要先调用Resolve()
,这会丢失所有通用信息。然后属性类型是T
(而不是int
),这当然会使后来的代码失败。
Mono.Cecil有GenericInstanceType
和GenericInstanceMethod
,但没有相应的属性。我尝试使用module.Import(prop.PropertyType, type)
(将type
作为通用参数提供程序),但这不起作用。
您对我如何解决实际属性类型有任何想法吗?请注意,它可能与T
,T
本身完全无关,或者内置T
(例如List<T>
)。理想情况下,它将type
作为TypeReference
工作 - 这样我就不必为非泛型和通用案例编写单独的代码。
答案 0 :(得分:1)
基于https://stackoverflow.com/a/16433452/613130,可能基于https://groups.google.com/d/msg/mono-cecil/QljtFf_eN5I/YxqLAk5lh_cJ,它应该是:
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Rocks;
namespace Utilities
{
public class EmitFactoriesAttribute : Attribute
{
public readonly Type[] Types;
public EmitFactoriesAttribute()
{
}
public EmitFactoriesAttribute(params Type[] types)
{
Types = types;
}
}
public class MagicPropertyAttribute : Attribute
{
}
public static class EmitFactories
{
public static void WorkOnAssembly(string path)
{
AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly("ConsoleApplication4.exe");
ModuleDefinition module = assembly.MainModule;
TypeDefinition emitFactoriesAttribute = module.Import(typeof(EmitFactoriesAttribute)).Resolve();
TypeDefinition magicPropertyAttribute = module.Import(typeof(MagicPropertyAttribute)).Resolve();
foreach (TypeDefinition type in module.Types)
{
CustomAttribute emitFactory = type.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == emitFactoriesAttribute.MetadataToken);
if (emitFactory == null)
{
continue;
}
TypeReference typeRef = type;
TypeReference[] replacementTypes;
if (emitFactory.ConstructorArguments.Count != 0)
{
var temp = ((CustomAttributeArgument[])emitFactory.ConstructorArguments[0].Value);
replacementTypes = Array.ConvertAll(temp, x => (TypeReference)x.Value);
}
else
{
replacementTypes = new TypeReference[0];
}
if (replacementTypes.Length != type.GenericParameters.Count)
{
throw new NotSupportedException();
}
if (replacementTypes.Length != 0)
{
typeRef = typeRef.MakeGenericInstanceType(replacementTypes);
}
foreach (PropertyDefinition prop in type.Properties)
{
CustomAttribute magicProperty = prop.CustomAttributes.SingleOrDefault(x => x.AttributeType.MetadataToken == magicPropertyAttribute.MetadataToken);
if (magicProperty == null)
{
continue;
}
MethodReference getter = prop.GetMethod;
MethodReference setter = prop.SetMethod;
if (replacementTypes.Length != 0)
{
if (getter != null)
{
getter = getter.MakeHostInstanceGeneric(replacementTypes);
}
if (setter != null)
{
setter = setter.MakeHostInstanceGeneric(replacementTypes);
}
}
}
}
}
}
public static class TypeReferenceExtensions
{
// https://stackoverflow.com/a/16433452/613130
public static MethodReference MakeHostInstanceGeneric(this MethodReference self, params TypeReference[] arguments)
{
var reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericInstanceType(arguments))
{
HasThis = self.HasThis,
ExplicitThis = self.ExplicitThis,
CallingConvention = self.CallingConvention
};
foreach (var parameter in self.Parameters)
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
foreach (var generic_parameter in self.GenericParameters)
reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
return reference;
}
}
// Test
[EmitFactories(typeof(int), typeof(long))]
public class Class<TKey, TValue>
{
[MagicProperty]
Dictionary<TKey, TValue> Property1 { get; set; }
[MagicProperty]
List<TValue> Property2 { get; set; }
}
}
您尚未定义EmitFactoriesAttribute
的格式,因此我将其编写为EmitFactoriesAttribute(params Type[] types)
,以便能够接受Class<TKey, TValue>
等案例的多次替换。
最后我不是直接操纵属性:我正在操纵它的getter和setter。
我无法测试它,因为我没有工厂......