在泛型类(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有GenericInstanceTypeGenericInstanceMethod,但没有对应的属性。我尝试使用module.Import(prop.PropertyType, type)(给予type作为通用参数提供程序),但这不起作用。

你对我如何解决实际的属性类型有什么想法吗?注意,它可以与TT本身完全无关,也可以包含T(例如List<T>)。理想情况下,它可以将type作为TypeReference来工作-这样我就不必为非泛型和泛型情况编写单独的代码。

在泛型类(Mono.Cecil)中获取属性类型

基于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。

我无法测试它,因为我没有工厂…