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

时间:2015-05-16 19:00:34

标签: c# generics mono.cecil

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

您对我如何解决实际属性类型有任何想法吗?请注意,它可能与TT本身完全无关,或者内置T(例如List<T>)。理想情况下,它将type作为TypeReference工作 - 这样我就不必为非泛型和通用案例编写单独的代码。

1 个答案:

答案 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。

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