Is it possible to write generic CIL instructions which will convert instances of any type (both value and reference) to System.String?
In particular, I'm interested in Mono.Cecil code which will inject those instructions into a method.
Analyzing a generic method I came up with these Mono.Cecil calls:
(it's supposed to convert the i-th method parameter to string)
System.Reflection.MethodInfo to_string_method_info = typeof( System.Object ).GetMethod( "ToString" );
Mono.Cecil.MethodReference to_string_reference = injectible_assembly.MainModule.Import( to_string_method_info );
Mono.Cecil.TypeReference argument_type = method_definition.Parameters[ i ].ParameterType;
method_definition.Body.Instructions.Add( processor.Create( Mono.Cecil.Cil.OpCodes.Constrained, argument_type ) );
method_definition.Body.Instructions.Add( processor.Create( Mono.Cecil.Cil.OpCodes.Callvirt, to_string_reference ) );
However, when debugging I get an exception from the injected method that the "JIT compiler encountered internal limitation".
Edit:
Also important: note that I am using typeof(object).GetMethod(...)
, not typeof(T).GetMethod(...)
- your line argument_type.GetType().GetMethod( "ToString" );
looks very suspect IMO.
I suspect the issue is that you are loading a local/argument, rather than the address of a local/argument - in the line immediately before what is shown. Constrained
needs this so that it can correctly perform the static-call implementation; for the virtual-call implementation, it can simply dereference this to get the actual reference.
Other than that: Constrained
should work fine - see below (in particular, note the Ldarga_S
). Of course, another option is to use Box
, but this will have more overhead. Constrained
is the ideal way of calling ToString
on an arbitrary type.
using System;
using System.Reflection.Emit;
public class RefTypeNoImpl { }
public class RefTypeImpl { public override string ToString() { return "foo"; } }
public struct ValTypeNoImpl { }
public struct ValTypeImpl { public override string ToString() { return "bar"; } }
static class Program
{
static void Main()
{
Test<RefTypeNoImpl>();
Test<RefTypeImpl>();
Test<ValTypeNoImpl>();
Test<ValTypeImpl>();
}
static void Test<T>() where T : new()
{
var dm = new DynamicMethod("foo", typeof(string), new[] { typeof(T) });
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarga_S, 0);
il.Emit(OpCodes.Constrained, typeof(T));
il.Emit(OpCodes.Callvirt, typeof(object).GetMethod("ToString"));
il.Emit(OpCodes.Ret);
var method = (Func<T, string>)dm.CreateDelegate(typeof(Func<T, string>));
Console.WriteLine(method(new T()));
}
}