Создание динамического типа из TypeBuilder с базовым классом и дополнительными полями генерирует исключение

Я пытаюсь создать динамический тип на основе существующего типа, который содержит только общедоступные поля. Новый динамический тип также должен наследовать от другого базового типа, который имеет только полностью реализованный метод.

Я создаю TypeBuilder, определяя базовый тип, затем добавляю к нему общедоступные поля и, наконец, вызываю CreateType(). В результате появляется следующее сообщение об ошибке:

«Не удалось загрузить тип InternalType из сборки MyDynamicAssembly, Version = 0.0.0.0, Culture = нейтральный, PublicKeyToken = null, потому что для поля« first »не было задано явное смещение».

Для меня это означает, что метод CreateType ищет публичное поле «первым» в базовом классе, что является проблемой, потому что его там нет. Почему он считает, что добавленное поле должно быть в базовом классе? Или я неправильно понимаю исключение?

Вот код:

public class sourceClass
{
    public Int32 first = 1;
    public Int32 second = 2;
    public Int32 third = 3;
}

public static class MyConvert
{
    public static object ToDynamic(object sourceObject, out Type outType)
    {
        // get the public fields from the source object
        FieldInfo[] sourceFields = sourceObject.GetType().GetFields();

        // get a dynamic TypeBuilder and inherit from the base type
        AssemblyName assemblyName
            = new AssemblyName("MyDynamicAssembly");
        AssemblyBuilder assemblyBuilder
            = AppDomain.CurrentDomain.DefineDynamicAssembly(
                assemblyName,
                AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder
            = assemblyBuilder.DefineDynamicModule("MyDynamicModule");
        TypeBuilder typeBuilder
            = moduleBuilder.DefineType(
                "InternalType",
                TypeAttributes.Public
                | TypeAttributes.Class
                | TypeAttributes.AutoClass
                | TypeAttributes.AnsiClass
                | TypeAttributes.ExplicitLayout,
                typeof(SomeOtherNamespace.MyBase));

        // add public fields to match the source object
        foreach (FieldInfo sourceField in sourceFields)
        {
            FieldBuilder fieldBuilder
                = typeBuilder.DefineField(
                    sourceField.Name,
                    sourceField.FieldType,
                    FieldAttributes.Public);
        }

        // THIS IS WHERE THE EXCEPTION OCCURS
        // create the dynamic class
        Type dynamicType = typeBuilder.CreateType();

        // create an instance of the class
        object destObject = Activator.CreateInstance(dynamicType);

        // copy the values of the public fields of the
        // source object to the dynamic object
        foreach (FieldInfo sourceField in sourceFields)
        {
            FieldInfo destField
                = destObject.GetType().GetField(sourceField.Name);
            destField.SetValue(
                destObject,
                sourceField.GetValue(sourceField));
        }

        // give the new class to the caller for casting purposes
        outType = dynamicType;

        // return the new object
        return destObject;
    }

person dtaylor    schedule 27.08.2013    source источник
comment
не могли бы вы показать, как вы используете метод ToDynamic (..), пожалуйста?   -  person    schedule 27.04.2020
comment
Спасибо за эту очень хорошую тему. Но разве вы не копируете новый класс с базовым классом, а не наследуете от базового класса?   -  person    schedule 27.04.2020


Ответы (1)


Хорошо, я понял это сразу после публикации. Я действительно неправильно прочитал сообщение об ошибке. Фактически, это не имело ничего общего с унаследованным базовым классом.

Создавая тип, я указал обязательный атрибут TypeAttributes.ExplicitLayout. К сожалению, я не осознавал, что мне также нужно было добавить смещение к каждому полю при их создании. Сообщение об исключении было полностью точным. Извините за ложную тревогу. Исправленный код выглядит следующим образом:

public class SourceClass
{
    public Int32 first = 1;
    public Int32 second = 2;
    public Int32 third = 3;
}

public static class MyConvert
{
    public static object ToDynamic(object sourceObject, out Type outType)
    {
        Int32 fieldOffset = 0;

        // get the public fields from the source object
        FieldInfo[] sourceFields = sourceObject.GetType().GetFields();

        // get a dynamic TypeBuilder and inherit from the base type
        AssemblyName assemblyName
            = new AssemblyName("MyDynamicAssembly");
        AssemblyBuilder assemblyBuilder
            = AppDomain.CurrentDomain.DefineDynamicAssembly(
                assemblyName,
                AssemblyBuilderAccess.Run);
        ModuleBuilder moduleBuilder
            = assemblyBuilder.DefineDynamicModule("MyDynamicModule");
        TypeBuilder typeBuilder
            = moduleBuilder.DefineType(
                "InternalType",
                TypeAttributes.Public
                | TypeAttributes.Class
                | TypeAttributes.AutoClass
                | TypeAttributes.AnsiClass
                | TypeAttributes.ExplicitLayout,
                typeof(SomeOtherNamespace.MyBase));

        // add public fields to match the source object
        foreach (FieldInfo sourceField in sourceFields)
        {
            FieldBuilder fieldBuilder
                = typeBuilder.DefineField(
                    sourceField.Name,
                    sourceField.FieldType,
                    FieldAttributes.Public);
            fieldBuilder.SetOffset(fieldOffset);
            fieldOffset++;
        }

        // create the dynamic class
        Type dynamicType = typeBuilder.CreateType();

        // create an instance of the class
        object destObject = Activator.CreateInstance(dynamicType);

        // copy the values of the public fields of the
        // source object to the dynamic object
        foreach (FieldInfo sourceField in sourceFields)
        {
            FieldInfo destField
                = destObject.GetType().GetField(sourceField.Name);
            destField.SetValue(
                destObject,
                sourceField.GetValue(sourceObject));
        }

        // give the new class to the caller for casting purposes
        outType = dynamicType;

        // return the new object
        return destObject;
    }

РЕДАКТИРОВАТЬ: приведенный выше код не будет работать. Индекс поля указывается в байтах, поэтому при увеличении смещения вы должны делать это на размер поля следующим образом:

fieldOffset += sizeof(Int32);
person dtaylor    schedule 27.08.2013
comment
Почему требуется ExplicitLayout? - person svick; 27.08.2013
comment
svick, он требуется сторонней библиотеке, которая использует неуправляемый код для взаимодействия с программируемым логическим контроллером (ПЛК). Специально не спрашивал, может быть, хватит SequentialLayout. - person dtaylor; 30.08.2013
comment
CreateType выдает ошибку: «System.TypeLoadException» в mscorlib.dll. Он говорит, что мой тип имеет недопустимый формат, почему? - person vulkanino; 24.01.2014
comment
vulkanino, просто предложение, но вы должны попробовать начать новый протектор. Большинство людей не читают темы, которые уже отмечены как отвеченные. Удачи. - person dtaylor; 27.01.2014
comment
@dtaylor, возможно, вам стоит использовать TypeBuilder.SetParent(Type), чтобы сообщить новому унаследованному классу, что он унаследован. - person ; 27.04.2020