CustomAttributeBuilder构造函数参数无效

时间:2014-06-16 13:15:10

标签: c# reflection.emit

我正在编写一个类来根据其他类型但修改后的属性动态生成类型,在我将属性部分添加到问题之前它工作正常,但在我添加以下代码来处理属性之后我尝试创建CustomAttributeBuilder时获取System.ArgumentException

class ModifiedTypeGenerator {
  Dictionary<Type, Dictionary<PropertyInfo, List<Attribute>>> modelInfo;

  public List<Type> GenerateModifiedTypes(Dictionary<Type,Dictionary<PropertyInfo,List<Attribute>>> models) 
  {
      modelInfo = models;
      List<Type> toReturn = new List<Type>();
      foreach(Type model in modelInfo.Keys) 
      {
          TypeBuilder tb = CreateTypeBuilder(model);
          ConstructurBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
          foreach(PropertyInfo propertyInfo in modelInfo[model].Keys) 
          {
               CreateProperty(tb,model,propertyInfo);
          }
          Type newType = tb.CreateType();
          toReturn.Add(newType); 
      }
      return toReturn;
  }

  private TypeBuilder CreateTypeBuilder(Type model)
  {
     string typeName = "modified_" + model.Name;
     AssemblyName assemblyName = new AssemblyName(typeName);

     AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
     ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("Module");

    TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName,
                TypeAttributes.Public |
                TypeAttributes.Class |
                TypeAttributes.AutoClass |
                TypeAttributes.AnsiClass |
                TypeAttributes.BeforeFieldInit |
                TypeAttributes.AutoLayout,
                model
            );
        return typeBuilder;
    }

private void CreateProperty(TypeBuilder typeBuilder, Type model, PropertyInfo propertyInfo)
{
    FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyInfo.Name, propertyInfo.PropertyType, FieldAttributes.Public);
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyInfo.Name, PropertyAttributes.HasDefault, propertyInfo.PropertyType, null);

    MethodBuilder getMethodBuilder = typeBuilder.DefineMethod("get_" + propertyInfo.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyInfo.PropertyType, Type.EmptyTypes);
    MethodBuilder setMethodBuilder = typeBuilder.DefineMethod("set_" + propertyInfo.Name, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, null, new[] { propertyInfo.PropertyType });

    ILGenerator getIl = getMethodBuilder.GetILGenerator();

    getIl.Emit(OpCodes.Ldarg_0);
    getIl.Emit(OpCodes.Ldfld, fieldBuilder);
    getIl.Emit(OpCodes.Ret);


    ILGenerator setIl = setMethodBuilder.GetILGenerator();
    Label modifyProperty = setIl.DefineLabel();
    Label exitSet = setIl.DefineLabel();

    setIl.MarkLabel(modifyProperty);
    setIl.Emit(OpCodes.Ldarg_0);
    setIl.Emit(OpCodes.Ldarg_1);
    setIl.Emit(OpCodes.Stfld, fieldBuilder);

    setIl.Emit(OpCodes.Nop);
    setIl.MarkLabel(exitSet);
    setIl.Emit(OpCodes.Ret);

    propertyBuilder.SetGetMethod(getMethodBuilder);
    propertyBuilder.SetSetMethod(setMethodBuilder);
    AddAttributes(propertyBuilder,model,propertyInfo);
}


private void AddAttributes(PropertyBuilder propertyBuilder, Type model, PropertyInfo propertyInfo)
{
    foreach(var attribute in modelInfo[model][propertyInfo])
    {
       CustomAttributeData customAttributeData = 
                propertyInfo.CustomAttributes.First(x => x.AttributeType == attribute.GetType());
        var constructorArguments = 
            customAttributeData.ConstructorArguments.Select(x => x as object).ToArray();
        CustomAttributeBuilder customAttributeBuilder = 
            new CustomAttributeBuilder(customAttributeData.Constructor, constructorArguments);
        propertyBuilder.SetCustomAttribute(customAttributeBuilder);
    }
}
}

将通过此方式运行的示例类型是:

public class TestModel 
{
    [RangeAttribute(1,100)]
    public int Count { get; set;}
}

class Program
 {
    static void Main(string[] args)
    {
        List<Type> types = new List<Type>();
        types.Add(typeof(TestModel));
        GetModifiedTypes(types);
    }

    public static List<Type> GetModifiedTypes(List<Type> models) 
    {  
        Dictionary<Type, Dictionary<PropertyInfo, List<Attribute>>> modelInfo = new Dictionary<Type, Dictionary<PropertyInfo, List<Attribute>>>();
        foreach(Type model in models)
        {
            modelInfo.Add(model, new Dictionary<PropertyInfo, List<Attribute>>());

            foreach(PropertyInfo propertyInfo in model.GetProperties())
            {
                var customAttributeDataList = propertyInfo.GetCustomAttributes(typeof(Attribute), true).Select(x => x as Attribute).ToList();

                for (int i = 0; i < customAttributeDataList.Count(); i++)
                {
                    var atr= customAttributeDataList[i] as dynamic;
                    customAttributeDataList[i] = GetModifiedAttribute(atr);
                }
                modelInfo[model].Add(propertyInfo, customAttributeDataList);
            }                   
        }

        ModifiedTypeGenerator mtg = new ModifiedTypeGenerator();
        return mtg.GenerateModifiedTypes(modelInfo);
    }

    public static Attribute GetModifieAttribute(Attribute attribute)
    {
        return attribute;
    }

    public static RangeAttribute GetModifiedAttribute(RangeAttribute attribute)
    {
        Random r = new Random();
        return new RangeAttribute(r.Next(), r.Next());
    }
}

1 个答案:

答案 0 :(得分:1)

问题是由这段代码引起的:

var constructorArguments = customAttributeData
    .ConstructorArguments.Select(x => x as object).ToArray();

此处CustomAttributeData.ConstructorArguments returns an IList<CustomAttributeTypedArgument>,其元素(类型CustomAttributeTypedArgument)被装箱为object并尝试用作参数构造函数接受两个int32参数。由于它们实际上不是int32类型,因此抛出。

您最有可能要传递给属性构造函数的实际int32值存储在CustomAttributeTypedArgument.Value中。因此固定代码如下所示:

var constructorArguments = customAttributeData
    .ConstructorArguments.Select(x => (object)x.Value).ToArray();