2016-12-28 4 views
3

私は動的に構築するコードのコンストラクタを構築する方法を以下の通りである:リフレクションエミット:この

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

は私が書くコードは次のとおりです。

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のソースは以下の通りです:

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

サンプルのインスタンスを作成できません。

誰でもこの問題を解決する手助けはできますか?

あなたのお手伝いをさせていただきます。

+0

「サンプルのインスタンスを作成できません」という意味はどうですか?実行中にエラーメッセージが表示されますか?コンパイルエラーまたは警告? –

+0

質問を編集してメッセージを挿入してください –

+0

{"不正な形式のプログラムを読み込もうとしました(HRESULTの例外:0x8007000B)"} –

答えて

2

このエラーの原因は、オープンジェネリック型のコンストラクタの呼び出しです。ジェネリックパラメータとしてTypeBuilderで閉じたジェネリック型のコンストラクタを取得する必要があります。 hereを説明しているConstructorInfoを取得する際に問題があります。

だから、解決策は、以下のパラメータを(@TonyTHONGは、すでに述べたように)TypeBuilder.GetConstructor(Type, ConstructorInfo)静的メソッドを呼び出すことです:

  • Typeは、あなたのケースtypeof(ObservableTestCollection<>).MakeGenericType(typeBuilder)には、TypeBuilderにジェネリック型を閉じなければなりませんでした。
  • ConstructorInfotypeof(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はタイプビルダです。

generic引数が "MakeGenericType"ではなくtypebuilderである場合は、TypeBuilder.GetConstructor(Type, ConstructorInfo)を使用して汎用コンストラクタを取得してください。

+0

こんにちはトニー、 var ci = TypeBuilder.GetConstructor(genericTypeOTS、ctorBuilder); '私は次のようなエラーが発生します:**指定されたコンストラクタはジェネリック型定義で宣言されなければなりません。 パラメータ名:コンストラクタ**。私は、サンプル型はジェネリック型ではなく、 "ObservableTestCollection"型がジェネリック型であると思います。ありがとう –

+0

@ JoonwK第2引数はオープンジェネリック型( 'ObservableTestCollection <>')から 'ConstructorInfo'でなければなりません。私は私の答えに詳細な説明を加えました。 –

+0

こんにちは、あなたはすでにこの記事で同様のケースがあります:http://stackoverflow.com/questions/41055488/how-to-define-a-self-referenced-type-property-with-reflection-emit-in-cシャープ –