I have this structure that I wrote a couple of months ago that generates a class to call this performance service. Framework consumers create an interface with methods, annotate attributes, and call the factory method, which creates an implementation of an interface that they can use to call this performance service. The service only supports two lines of data and a long one. I use emit reflection with collectible assemblies to create a class that implements the interface.
Everything works well, but today someone told me that they get AV when they tried to pass an enumeration that would be converted to a string. There is a check in the code to find out if the type is a value type, and if so, click the address (ldarga or ldflda depending on the interface created by the consumer), and then call ToString. So I created a small debugging application, and I saw that the C # compiler would enable the enumeration, and then call ToString on the enum box.
Since I'm a little confused. Is there a way to incorrectly handle type values? Is the C # IL compiler generating the correct way for toString on an enum? Are there other special cases like
Update with the answer: so it seems like I need to see if the type implements the tostring value, and if it is not inserted. For value types, I assume this applies to object methods, tostring, gethashcode, equals.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
namespace ConsoleApplication15
{
public struct H
{
}
class Program
{
static void Main(string[] args)
{
TestCorrect<H>(new H());
TestCorrect<int>(10);
Console.ReadLine();
}
private static void TestCorrect<T>(T t)
where T : struct
{
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
if (method.DeclaringType == typeof(T))
{
i.Emit(OpCodes.Ldarga, 0);
i.Emit(OpCodes.Call, method);
}
else
{
i.Emit(OpCodes.Ldarg_0);
i.Emit(OpCodes.Box, typeof(T));
i.Emit(OpCodes.Callvirt, method);
}
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
private static void Test<T>(T t)
where T : struct
{
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
i.Emit(OpCodes.Ldarga, 0);
i.Emit(OpCodes.Call, method);
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
private static void TestBox<T>(T t)
where T : struct
{
MethodInfo method = typeof(T).GetMethod(
"ToString",
BindingFlags.Public | BindingFlags.Instance,
null,
Type.EmptyTypes,
null);
var m = new DynamicMethod("x", typeof(string), new[] { typeof(T) });
var i = m.GetILGenerator();
i.Emit(OpCodes.Ldarg_0);
i.Emit(OpCodes.Box, typeof(T));
i.Emit(OpCodes.Callvirt, method);
i.Emit(OpCodes.Ret);
string result = (m.CreateDelegate(typeof(Func<T, string>)) as Func<T, string>)(t);
Console.WriteLine(result);
}
}
}