.NET TypeBuilder - VerificationException: операция может дестабилизировать среду выполнения - PullRequest
2 голосов
/ 25 августа 2010

Мне нужно создать тип во время выполнения, используя TypeBuilder.Этот тип должен реализовывать определенный интерфейс, чтобы можно было обрабатывать экземпляры этого динамического типа равномерно во время компиляции.

Интерфейс должен возвращать массив объектов, заполненных значениями определенных полей этого типа.

Интерфейс, который предполагается реализовать, определяется следующим образом:

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, VerificationException с сообщением «Операция может дестабилизировать среду выполнения».брошенЯ понятия не имею, что вызывает это.Может ли кто-нибудь помочь?

Спасибо!

Ответы [ 2 ]

2 голосов
/ 25 августа 2010

Код операции stelem.ref сохраняет ссылку на объект в массиве. Это означает, что если у вас есть поля с типами значений (например, int s), то вам необходимо убедиться, что они упакованы, прежде чем сохранять их в массиве. Поэтому вы должны добавить что-то вроде

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

непосредственно перед инструкцией stelem.ref.

Кроме того, хотя это не должно вызывать исключение проверки, обратите внимание, что имена методов ваших свойств должны быть get_{Name} и set_{Name}, а не {Name}_get и {Name}_set.

1 голос
/ 25 августа 2010

Вы не объявили локальную переменную, но используете ее (Stloc_0, Ldloc_0).

Вставьте это право перед генерацией кода IL:

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