Is there a generic CIL code to convert any type in

2019-05-03 11:18发布

问题:

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".

回答1:

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