Reputation: 4295
I'm creating a dynamic function to create an object at runtime given an object[] of constructor params. I keep getting the generic exception 'Operation could destablise the runtime' and I can't see what I've done wrong.
The method works fine if the created object needs no constructor arguments - so the problem must be in the code in the for loop.
The code indexes into the given object[] putting the object onto the stack after which the ctor is called and the object returned.
Any ideas???
internal static Func<object[], object> CreateObjectFactoryMethodWithCtorParams(ConstructorInfo ctor, int ctorArgsLength)
{
Func<object[], object> factoryMethod = null;
if (ctor != null)
{
var dm = new DynamicMethod(string.Format("_CreationFacotry_{0}", Guid.NewGuid()), typeof(object), new Type[] { typeof(object[])}, true);
var il = dm.GetILGenerator();
il.DeclareLocal(typeof(int));
il.DeclareLocal(typeof(object));
il.BeginExceptionBlock();
il.Emit(OpCodes.Ldc_I4_0); // [0]
il.Emit(OpCodes.Stloc_0); //[nothing]
for (int i = 0; i < ctorArgsLength; i++)
{
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Stloc_0); // [args][index]
il.Emit(OpCodes.Ldarg_0); //[args]
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
}
il.Emit(OpCodes.Newobj, ctor); //[new-object]
il.Emit(OpCodes.Stloc_1); // nothing
il.BeginCatchBlock(ExceptionType); // stack is Exception
il.Emit(OpCodes.Ldloc_0); // stack is Exception, index
il.EmitCall(OpCodes.Call, EmitGeneratorType.GetMethod("ThrowFactoryException"), null);
il.EndExceptionBlock();
il.Emit(OpCodes.Ldloc_1); //[new-object]
il.Emit(OpCodes.Ret);
factoryMethod = (Func<object[], object>)dm.CreateDelegate(typeof(Func<object[], object>));
}
else
{
throw new EmitGeneratorException("Cannot create instance factory for a null ctor instance");
}
return factoryMethod;
}
private static void EmitInt32(ILGenerator il, int value)
{
switch (value)
{
case -1: il.Emit(OpCodes.Ldc_I4_M1); break;
case 0: il.Emit(OpCodes.Ldc_I4_0); break;
case 1: il.Emit(OpCodes.Ldc_I4_1); break;
case 2: il.Emit(OpCodes.Ldc_I4_2); break;
case 3: il.Emit(OpCodes.Ldc_I4_3); break;
case 4: il.Emit(OpCodes.Ldc_I4_4); break;
case 5: il.Emit(OpCodes.Ldc_I4_5); break;
case 6: il.Emit(OpCodes.Ldc_I4_6); break;
case 7: il.Emit(OpCodes.Ldc_I4_7); break;
case 8: il.Emit(OpCodes.Ldc_I4_8); break;
default:
if (value >= -128 && value <= 127)
{
il.Emit(OpCodes.Ldc_I4_S, (sbyte)value);
}
else
{
il.Emit(OpCodes.Ldc_I4, value);
}
break;
}
}
Calling code
Func<object[], object> factoryFunction = GetFunction(someCtor, new object[] { arg1, arg2});
var obj = factoryFunction(new object[] {new SomeClass, "A String" }); //input ctor args
Upvotes: 4
Views: 5023
Reputation: 1064204
It works fine for me, as long as I make all the constructor parameters object
:
class SomeClass {
public SomeClass(object s, object t) { }
}
static void Main()
{
var someCtor = typeof(SomeClass).GetConstructors()[0];
Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor, someCtor.GetParameters().Length);
var obj = factoryFunction(new object[] {"A String", 123 });
}
I think the problem is that you haven't done any conversions from the objects from the array to the actual constructor types, noting that you need to consider both reference types and value-types (unbox). Like so:
var parameters = ctor.GetParameters();
for (int i = 0; i < parameters.Length ; i++)
{
EmitInt32(il, i); // [index]
il.Emit(OpCodes.Stloc_0); // [nothing]
il.Emit(OpCodes.Ldarg_0); //[args]
EmitInt32(il, i); // [args][index]
il.Emit(OpCodes.Ldelem_Ref); // [item-in-args-at-index]
var paramType = parameters[i].ParameterType;
if (paramType != typeof(object))
{
il.Emit(OpCodes.Unbox_Any, paramType); // same as a cast if ref-type
}
}
il.Emit(OpCodes.Newobj, ctor); //[new-object]
il.Emit(OpCodes.Stloc_1); // nothing
as a minor note: since you need to call .GetParameters()
, you should not pass in the parameter length as a parameter to the method; that is redundant, and could cause errors when wrong.
This then works with my exmaple:
class SomeClass {
public SomeClass(string s, int t) { }
}
static void Main()
{
var someCtor = typeof(SomeClass).GetConstructors()[0];
Func<object[], object> factoryFunction = CreateObjectFactoryMethodWithCtorParams(someCtor);
var obj = factoryFunction(new object[] {"A String", 123 });
}
Upvotes: 5