Creating a dynamic type from TypeBuilder with a base class and additional fields throws an exception

I am trying to create a dynamic type based on an existing type that contains only public fields. The new dynamic type should also inherit from another base type, which has only a fully implemented method.

I create a TypeBuilder by defining a base type, then add public fields to it, and finally call CreateType() . Received error message:

"Failed to load type 'InternalType' from assembly 'MyDynamicAssembly, Version = 0.0.0.0, Culture = neutral, PublicKeyToken = null' because the field 'first' was not given an explicit offset."

For me, this means that the CreateType method CreateType for the public field β€œfirst” in the base class, which is a problem because it is not there. Why does he think that the added field should be in the base class? Or do I not understand the exception?

Here is the code:

 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; } 
+6
source share
1 answer

Ok, I thought about it after posting. I really read the error message incorrectly. This, in fact, has nothing to do with the inherited base class.

When I created the type, I specified the attribute "TypeAttributes.ExplicitLayout", which is required. Unfortunately, I did not understand that I had to add an offset to each field when I created them. The exception message was completely accurate. Sorry for the false alarm. The corrected code follows:

 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; } 

EDIT: The above code will not work. The field index is in bytes, so when you increase the offset, you should do this to fit the field as follows:

 fieldOffset += sizeof(Int32); 
+8
source

Source: https://habr.com/ru/post/952595/


All Articles