2010-08-25 63 views
2

我需要在运行时使用TypeBuilder创建一个类型。这种类型应该实现一个特定的接口,以便可以在编译时统一处理这种动态类型的实例。.NET TypeBuilder - VerificationException:操作可能会破坏运行时的稳定性

接口应该返回一个填充了该类型中特定字段值的对象数组。是应该被实现被定义为

接口如下:

public interface ISelectable 
{ 
    object[] GetPrimaryKeysValues(); 
} 

这是我使用以产生用于所述接口的方法的代码:所述方法被认为

public static Type BuildTypeFromTable(Table tableToBuildTypeFrom) 
{ 
    AssemblyBuilder customTypesAssembly = 
      AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("CustomTypesAssembly"), AssemblyBuilderAccess.Run); 

    ModuleBuilder _moduleBuilder = customTypesAssembly.DefineDynamicModule("CustomTypesModule"); 

    TypeBuilder customTypeBuilder = _moduleBuilder.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Public | TypeAttributes.Class); 

    List<FieldBuilder> primaryKeyFields = new List<FieldBuilder>(); 

    //create a property for each column in the table 
    for (int i = 0; i < tableToBuildTypeFrom.Columns.Count; i++) 
    { 
     string propertyName = tableToBuildTypeFrom.Columns[i].Name; 
     //get a type of a property to create from a first row of the table 
     Type propertyType = tableToBuildTypeFrom.GetTypeOfColumnAtIndex(i); 

     //each property has to have a field to store its value in 
     FieldBuilder backingField = customTypeBuilder.DefineField(propertyName + "_field", propertyType, FieldAttributes.Private); 

     //body of a property getter 
     MethodBuilder getMethod = customTypeBuilder.DefineMethod(propertyName + "_get", MethodAttributes.Public | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes); 
     ILGenerator getIlGenerator = getMethod.GetILGenerator(); 
     getIlGenerator.Emit(OpCodes.Ldarg_0); 
     getIlGenerator.Emit(OpCodes.Ldfld, backingField); 
     getIlGenerator.Emit(OpCodes.Ret); 

     ///body of a property setter 
     MethodBuilder setMethod = customTypeBuilder.DefineMethod(propertyName + "_set", MethodAttributes.Public | MethodAttributes.HideBySig, null, new Type[] { propertyType }); 
     ILGenerator setIlGenerator = setMethod.GetILGenerator(); 
     setIlGenerator.Emit(OpCodes.Ldarg_0); 
     setIlGenerator.Emit(OpCodes.Ldarg_1); 
     setIlGenerator.Emit(OpCodes.Stfld, backingField); 
     setIlGenerator.Emit(OpCodes.Ret); 

     PropertyBuilder customProperty = customTypeBuilder.DefineProperty(propertyName, PropertyAttributes.None, propertyType, Type.EmptyTypes); 
     customProperty.SetGetMethod(getMethod); 
     customProperty.SetSetMethod(setMethod); 

     //save all primary key columns to avoid iterating over columns all over again 
     if (tableToBuildTypeFrom.Columns[i].IsPrimaryKey) 
     { 
      primaryKeyFields.Add(backingField); 
     } 
    } 

    customTypeBuilder.AddInterfaceImplementation(typeof(ISelectable)); 

    MethodBuilder getPrimaryKeysMethod = customTypeBuilder.DefineMethod("GetPrimaryKeysValues", MethodAttributes.Public | MethodAttributes.Virtual, typeof(object[]), null); 
    ILGenerator getPrimaryKeysMethodIlGenerator = getPrimaryKeysMethod.GetILGenerator(); 

    getPrimaryKeysMethodIlGenerator.DeclareLocal(typeof(object[])); 
    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldc_I4, primaryKeyFields.Count); 
    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Newarr, typeof(object)); 
    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Stloc_0); 

    for (int i = 0; i < primaryKeyFields.Count; i++) 
    { 
     getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldloc_0); 
     getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldc_I4, i); 

     getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldarg_0); 
     getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldfld, primaryKeyFields[i]); 

     getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Stelem_Ref); 
    } 

    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ldloc_0); 
    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Ret); 

    MethodInfo s = typeof(ISelectable).GetMethod("GetPrimaryKeysValues"); 
    customTypeBuilder.DefineMethodOverride(getPrimaryKeysMethod, s); 

    return customTypeBuilder.CreateType(); 
} 

的方式从MSDN取得。

现在,问题是每次我尝试调用GetPrimaryKeysValues方法时,带有消息'Operation的VerificationException可能会破坏运行时的稳定性。'被抛出。我不知道是什么导致它。有人可以帮忙吗?

谢谢!

回答

2

stelem.ref操作码存储一个对象引用到一个数组。这意味着如果您有值类型的字段(例如int s),那么您需要确保在将它们存储到数组中之前将它们装箱。因此,您应该在stelem.ref指令之前加上类似

if (primaryKeyFields[i].FieldType.IsValueType) { 
    getPrimaryKeysMethodIlGenerator.Emit(OpCodes.Box, primaryKeyFields[i].FieldType); 
} 

此外,虽然这不应该引起验证异常,请注意您的属性的方法名称应该是get_{Name}set_{Name},不{Name}_get{Name}_set

+0

谢谢你,这个伎俩! 我只需要改变你的代码 - 为了使它工作,Box操作码的Type参数应该是typeof(primaryKeyFields [i] .FieldType),而不是typeof(object)。否则,我得到了FatalExecutionEngineError。 我也改变了我的属性方法名称,正如你所建议的。这是一种命名约定吗? – 2010-08-25 21:16:47

+1

@Lotar - 是的,这是公共语言规范使用的命名约定(请参阅http://msdn.microsoft.com/en-us/library/12a7a7h3.aspx中的“访问者名称”部分)。我编辑了我的答案,以修复盒子指令使用的类型。 – kvb 2010-08-26 05:24:16

1

您没有声明局部变量,但是使用它(Stloc_0Ldloc_0)。

生成IL代码之前插入此右:

getPrimaryKeysMethodIlGenerator.DeclareLocal(typeof(object[])); 
+0

我已经添加了你的代码行,但它仍然不起作用。同样的异常不断抛出。 我已经更新了我的初始文章以反映您的建议,但它肯定是正确的。谢谢! – 2010-08-25 12:27:53

+0

@Lotar请把整个代码。因为ifter我添加了局部变量,你给它开始工作。 – Andrey 2010-08-25 13:09:51

+0

@Lotar我有一种感觉,你的堆栈是不平衡的,你有更多的推(ldXXX),然后弹出(其他) – Andrey 2010-08-25 13:12:58

相关问题