The code I want to build dynamically is as follow:
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;
}
The Source of ObservableTestCollection is as follow:
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;
}
}
The code I write is:
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);
I can't create instance of Sample.
Can anyone help me correct this problem?
Your help would be much appreciated.
Reason for this error is call of constructor for open generic type. You need to get constructor for closed generic type with
TypeBuilderas generic parameter. There are some problem with getting thisConstructorInfothat explained here.So solution is to call
TypeBuilder.GetConstructor(Type, ConstructorInfo)static method (as @TonyTHONG already mentioned) with following parameters:Typehad to be generic type closed onTypeBuilder, in your casetypeof(ObservableTestCollection<>).MakeGenericType(typeBuilder);ConstructorInfothat you can get from open generic type, in your casetypeof(ObservableTestCollection<>).You can see code example for your problem below:
Also please keep in mind that I've managed to run it only by placing
ObservableTestCollectionin different assembly from code that generatesSampleclass.If I'm not mistaken you are also generating
ObservableTestCollectionclass dynamically. So it may work without separating assemblies, especially if you use sameAssemblyBuilderfor them.