2016-12-28 33 views
3

我要動態地生成的代碼如下:反射發出:如何建立構造這個

public class Sample 
{ 
    public Sample() 
    { 
     Items = new ObservableTestCollection<Sample>(this); 
    } 
    public Sample(IEnumerable<Sample> source) 
    { 
     Items = new ObservableTestCollection<Sample>(this, source); 
    } 
    public ObservableTestCollection<Sample> Items; 
} 

ObservableTestCollection來源情況如下:

public class ObservableTestCollection<T> : ObservableCollection<T> 
{ 
    public T Parent;  
    public ObservableTestCollection(T parent) 
    { 
     Parent = parent; 
    } 
    public ObservableTestCollection(T parent, IEnumerable<T> source) : base(source) 
    { 
     Parent = parent; 
    } 
} 

我寫的代碼是:

const string assemblyName = "SampleAssembly"; 
const string fieldName = "Items"; 
const string typeName = "Sample"; 
const string assemblyFileName = assemblyName + ".dll"; 

AppDomain domain = AppDomain.CurrentDomain; 
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave); 
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName); 

TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public); 

Type[] ctorParameters = new Type[] { typeBuilder }; 
Type typeOfCTS = typeof(ObservableTestCollection<>); 
Type genericTypeOTS = typeOfCTS.MakeGenericType(typeBuilder); 


FieldBuilder fieldBuilder = typeBuilder.DefineField(fieldName, genericTypeOTS, FieldAttributes.Public); 

     //first constructor 
ConstructorBuilder ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes); 
ILGenerator generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes)); //call object constructor 

var ci = typeOfCTS.GetConstructors()[0]; 
generator.Emit(OpCodes.Newobj, ci);    
generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
generator.Emit(OpCodes.Ret); //return 

//second constructor 
var typeOfIE = typeof(IEnumerable<>); 
var genericTypeIE = typeOfIE.MakeGenericType(typeBuilder);   
ctorParameters = new Type[] {genericTypeIE }; 
ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorParameters); 

ctorParameters = new Type[] { typeBuilder, genericTypeIE }; 
generator = ctorBuilder.GetILGenerator(); 
generator.Emit(OpCodes.Ldarg_0); //load this 

ci = typeOfCTS.GetConstructors()[1]; 
generator.Emit(OpCodes.Newobj, ci); 
generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
generator.Emit(OpCodes.Ret); //return 
Type type = typeBuilder.CreateType(); 
var obj = Activator.CreateInstance(type); 
assemblyBuilder.Save(assemblyFileName); 

我無法創建Sample的實例。

任何人都可以幫我解決這個問題嗎?

您的幫助將不勝感激。

+0

你是什麼意思「我不能創建示例實例」?運行時是否有錯誤信息?編譯錯誤或警告? –

+0

請編輯您的問題並插入消息 –

+0

我收到的消息引用如下:{「試圖加載格式不正確的程序(來自HRESULT的異常:0x8007000B)」} –

回答

2

此錯誤的原因是打開泛型類型的構造函數的調用。您需要獲得與TypeBuilder作爲泛型參數的封閉泛型類型的構造函數。 獲取此ConstructorInfo時出現了一些問題,說明here

所以解決方法是調用TypeBuilder.GetConstructor(Type, ConstructorInfo)靜態方法(如@TonyTHONG已經提到)與下列參數:

  • Type必須是通用型封閉TypeBuilder,你的情況typeof(ObservableTestCollection<>).MakeGenericType(typeBuilder);
  • ConstructorInfo你可以從開放的泛型類型獲得,在你的案例typeof(ObservableTestCollection<>)

你可以看到的代碼示例下面的問題:

 const string assemblyName = "SampleAssembly"; 
     const string fieldName = "Items"; 
     const string typeName = "Sample"; 
     const string assemblyFileName = assemblyName + ".dll"; 

     var domain = AppDomain.CurrentDomain; 
     var assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave); 

     var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName); 
     var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public); 

     var typeOfCts = typeof(ObservableTestCollection<>); 
     var genericTypeOfCts = typeOfCts.MakeGenericType(typeBuilder); 

     var fieldBuilder = typeBuilder.DefineField(fieldName, genericTypeOfCts, FieldAttributes.Public); 

     //first constructor Sample() 
     var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Type.EmptyTypes); 
     var obsCtor1 = typeOfCts.GetConstructors().First(c => c.GetParameters().Length == 1); 
     obsCtor1 = TypeBuilder.GetConstructor(genericTypeOfCts, obsCtor1); //hack to get close generic type ctor with typeBuilder as generic parameter 

     var generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this for base type constructor 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructors().Single()); 

     generator.Emit(OpCodes.Ldarg_0); //load this for field setter 

     generator.Emit(OpCodes.Ldarg_0); //load this for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Newobj, obsCtor1); //call ObservableTestCollection constructor, it will put point to new object in stack 

     generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
     generator.Emit(OpCodes.Ret); //return 

     //second constructor Sample(IEnumerable<Sample> source) 
     var ctorParam = typeof(IEnumerable<>).MakeGenericType(typeBuilder); 
     ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { ctorParam }); 
     obsCtor1 = typeOfCts.GetConstructors().First(c => c.GetParameters().Length == 2); 
     obsCtor1 = TypeBuilder.GetConstructor(genericTypeOfCts, obsCtor1); //hack to get close generic type ctor with typeBuilder as generic parameter 

     generator = ctorBuilder.GetILGenerator(); 
     generator.Emit(OpCodes.Ldarg_0); //load this for base type constructor 
     generator.Emit(OpCodes.Call, typeof(object).GetConstructors().Single()); 

     generator.Emit(OpCodes.Ldarg_0); //load this for field setter 

     generator.Emit(OpCodes.Ldarg_0); //load this for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Ldarg_1); //load IEnumerable for ObservableTestCollection constructor 
     generator.Emit(OpCodes.Newobj, obsCtor1); //call ObservableTestCollection constructor, it will put point to new object in stack 

     generator.Emit(OpCodes.Stfld, fieldBuilder); // store into Items 
     generator.Emit(OpCodes.Ret); //return 


     var type = typeBuilder.CreateType(); 
     var obj1 = Activator.CreateInstance(type); 

     var parameter = Activator.CreateInstance(typeof(List<>).MakeGenericType(type)); 
     var obj2 = Activator.CreateInstance(type, parameter); 
     assemblyBuilder.Save(assemblyFileName); 

也請記住,我已經成功只有從產生Sample類代碼放置在ObservableTestCollection不同的裝配來運行它。

如果我沒有弄錯,你也會動態地生成ObservableTestCollection類。所以它可以在不分離組件的情況下工作,特別是如果您爲它們使用相同的AssemblyBuilder

+0

謝謝安德烈。這十分完美!! –

+0

我構建了ObservableTestCollection並使用相同的AssemblyBuilder創建了Sample,並發現它可行。這對我來說也是一個很大的幫助。再次感謝。 –

2

您的程序無效,因爲您嘗試創建一個實例「ObservableTestCollection of Sample」,但Sample是一個類型生成器。

如果泛型參數是類型構建器而不是「MakeGenericType」,請使用TypeBuilder.GetConstructor(Type, ConstructorInfo)來獲得泛型構造函數。

+0

嗨,託尼,我試着用' var ci = TypeBuilder.GetConstructor(genericTypeOTS,ctorBuilder);'我得到如下錯誤:**指定的構造函數必須在泛型類型定義中聲明。 參數名稱:構造函數**。我認爲Sample類型不是泛型類型,但類型「ObservableTestCollection」是泛型類型。謝謝 –

+0

@JoonwK第二個參數應該是來自開放泛型類型('ObservableTestCollection <>')的'ConstructorInfo'。我在答案中添加了詳細的答案示例說明。 –

+0

嗨,你已經在這篇文章中有類似的情況:http://stackoverflow.com/questions/41055488/how-to-define-a-self-referenced-type-property-with-reflection-emit-in-c -sharp –