2013-08-27 26 views
6

Próbuję utworzyć typ dynamiczny na podstawie istniejącego typu zawierającego tylko pola publiczne. Nowy typ dynamiczny musi również dziedziczyć z innego typu bazowego, który ma tylko w pełni zaimplementowaną metodę.Tworzenie typu dynamicznego z TypeBuilder z klasą podstawową i dodatkowymi polami generuje wyjątek

Tworzę TypeBuilder określając typ bazowy, a następnie dodaję do niego publiczne pola i na koniec zadzwonię pod numer CreateType(). Otrzymany komunikat o błędzie:

„Nie można załadować typu 'InternalType' z zespołem 'MyDynamicAssembly, Version = 0.0.0.0, Culture = neutral, TokenKluczaPublicznego = null', ponieważ pole 'pierwsze' nie był podawany był wyraźne przesunięcie. "

Dla mnie oznacza to, że metoda CreateType szuka publicznego pola "pierwszy" w klasie bazowej, co stanowi problem, ponieważ go nie ma. Dlaczego sądzi, że dodane pole powinno należeć do klasy bazowej? Czy nie rozumiem wyjątku?

Oto kod:

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

Odpowiedz

8

Ok, pomyślałem to na chwile po zaksięgowaniu. Naprawdę błędnie odczytałem komunikat o błędzie. W rzeczywistości nie miało to nic wspólnego z odziedziczoną klasą podstawową.

Po utworzeniu typu I określono wymagany atrybut "TypeAttributes.ExplicitLayout". Niestety, nie zdawałem sobie sprawy, że muszę dodać przesunięcie do każdego pola, kiedy je stworzyłem. Komunikat o wyjątku był całkowicie dokładny. Przepraszamy za fałszywy alarm. Poprawiony kod:

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

EDYCJA: Powyższy kod nie będzie działać. Indeks pola znajduje się w bajtach, więc gdy zwiększasz offset, powinieneś zrobić to według rozmiaru pola, jak poniżej:

fieldOffset += sizeof(Int32); 
+1

Dlaczego wymagany jest 'ExplicitLayout'? – svick

+0

svick, jest wymagany przez bibliotekę 3rd party, która używa niezarządzanego kodu do połączenia z programowalnym kontrolerem logicznym (PLC). Nie zapytałem konkretnie, być może wystarczy SequentialLayout. – dtaylor

+0

'CreateType' zgłasza błąd:" System.TypeLoadException "w mscorlib.dll. Mówi, że mój typ ma nieprawidłowy format, dlaczego? – vulkanino