在泛型类(Mono.Cecil)中获取属性类型
本文关键字:获取 属性 类型 Cecil 泛型类 Mono | 更新日期: 2023-09-27 17:49:33
我使用Mono。自动生成(大量的、简单的、通用的)工厂方法,为库提供方便的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
来工作-这样我就不必为非泛型和泛型情况编写单独的代码。
基于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。
我无法测试它,因为我没有工厂…