I am trying create a delegate representation of constructor by emitting a Dynamic Method, which has to match this very "loosely-typed" signature so it can be used with any kind of parametrized constructor:
public delegate Object ParamsConstructorDelegate(params object[] parameters);
and the code for this creating the delegate looks like (note this is for Silverlight)
public static ParamsConstructorDelegate CreateDelegate(ConstructorInfo constructor)
{
Guard.ArgumentNotNull(constructor, "constructor");
Guard.ArgumentValue(constructor.GetParameters().Length == 0, MUSTBE_PARAMETERIZED_CONSTRUCTOR);
var _argumentTypes = new Type[] { typeof(object[]) };
var _parameters = constructor.GetParameters();
var _parameterTypes = _parameters.Select((p) => p.ParameterType).ToArray();
var _sourceType = constructor.DeclaringType;
var _method = new DynamicMethod(constructor.Name, _sourceType, _argumentTypes);
var _gen = _method.GetILGenerator();
for (var _i = 0; _i < _parameters.Length; _i++)
{
if (_parameters[_i].IsOut || _parameterTypes[_i].IsByRef)
{
if (_i < 128)
{
_gen.Emit(OpCodes.Ldarga_S, (byte)_i);
}
else
_gen.Emit(OpCodes.Ldarga, _i);
}
else
{
switch (_i)
{
case 0:
_gen.Emit(OpCodes.Ldarg_0, _i);
break;
case 1:
_gen.Emit(OpCodes.Ldarg_1, _i);
break;
case 2:
_gen.Emit(OpCodes.Ldarg_2, _i);
break;
case 3:
_gen.Emit(OpCodes.Ldarg_3, _i);
break;
default:
if (_i < 128)
_gen.Emit(OpCodes.Ldarg_S, (byte)_i);
else
_gen.Emit(OpCodes.Ldarg, _i);
break;
}
}
}
_gen.Emit(OpCodes.Newobj, constructor);
_gen.Emit(OpCodes.Ret); ;
return (ParamsConstructorDelegate)_method.CreateDelegate(typeof(ParamsConstructorDelegate));
}
Now, I'm getting a "Operation could destabilize the runtime." verification exception, obviously the IL is wrong, so I hoping someone could correct me.
Thanks
I can see two problems; firstly you don't need the _i
for the Ldarg_0
thru Ldarg_3
cases (it is implicit). Secondly - your delegate only has one arg (the array). You're going to need to get the items out of the array and cast - something like below (which handles pass-by-value only; for ref
/ out
you'll have to define a local and use stloc / ldloca / etc):
using System;
using System.Reflection.Emit;
public delegate object ParamsConstructorDelegate(params object[] parameters);
public class Foo
{
string s;
int i;
float? f;
public Foo(string s, int i, float? f)
{
this.s = s;
this.i = i;
this.f = f;
}
}
static class Program
{
static void Main()
{
var ctor = Build(typeof(Foo));
Foo foo1 = (Foo)ctor("abc", 123, null);
Foo foo2 = (Foo)ctor(null, 123, 123.45F);
}
static ParamsConstructorDelegate Build(Type type)
{
var mthd = new DynamicMethod(".ctor", type,
new Type[] { typeof(object[]) });
var il = mthd.GetILGenerator();
var ctor = type.GetConstructors()[0]; // not very robust, but meh...
var ctorParams = ctor.GetParameters();
for (int i = 0; i < ctorParams.Length; i++)
{
il.Emit(OpCodes.Ldarg_0);
switch (i)
{
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: il.Emit(OpCodes.Ldc_I4, i); break;
}
il.Emit(OpCodes.Ldelem_Ref);
Type paramType = ctorParams[i].ParameterType;
il.Emit(paramType.IsValueType ? OpCodes.Unbox_Any
: OpCodes.Castclass, paramType);
}
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Ret);
return (ParamsConstructorDelegate)
mthd.CreateDelegate(typeof(ParamsConstructorDelegate));
}
}
For info - I'm lazy - if I want to know what IL to write I write it in C# and then load it into reflector. For example, to do this I wrote a method:
static object CreateFoo(object[] vals)
{
return new Foo((string)vals[0], (int)vals[1], (float?)vals[2]);
}
and reversed it from there
I find it very difficult to understand what the error message "Operation could destabilize the runtime" means when using Reflection.Emit
- the CLR doesn't give much useful information here. One trick that you can use to get more information about the problem is to modify your code, so that it emits the code to some temporary assembly (in addition to emitting a dynamic delegate) when running in Debug mode.
Then you can use the peverify
tool (from the Visual Studio Command Line), which usually gives you more information about the issues with the generated IL code:
> peverify assembly.dll
You'll need to use classes like AssemblyBuilder
and ModuleBuilder
to produce an assembly. Then you can run the core part (which uses ILGenerator
) two times to generate dynamic delegate (for actual running) and temporary assembly (for debugging). I believe that peverify
gives you a much better information.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With