I'm currently working on an extension on the Moq framework to be also to mock the implementation of non virtual methods. I currently already have this working by obtaining the Method Handle of the original Method and swapping this with the pointer of a user defined Func.
One issue I'm still running into is that when I create the Func in the Moq internal code (inside a class that uses Generics) I run into an issue with RuntimeHelpers.PrepareMethod. (The Func needs to be prepared before we can execute the pointer swap).
When I create exactly the same Func in a normal class (e.g. Program) everything works fine.
Further investigating the issue traced this back to wether the call class had generic arguments or not.
Exception being thrown:
An unhandled exception of type 'System.ArgumentException' occurred in mscorlib.dll
Additional information: The given generic instantiation was invalid.
I have isolated the problem in the following codeblock:
class Program
{
static void Main(string[] args)
{
new WithoutGeneric().GoExecute();
new WithGeneric<string>().GoExecute();
}
}
public class WithoutGeneric
{
public void GoExecute()
{
//Works fine
StaticMethods.PrepareThisFunc(() => "Test");
}
}
public class WithGeneric<T>
{
public void GoExecute()
{
//Breaks
StaticMethods.PrepareThisFunc(() => "Test");
}
}
public static class StaticMethods
{
public static void PrepareThisFunc(Func<string> theFunc)
{
RuntimeHelpers.PrepareMethod(theFunc.Method.MethodHandle);
}
}
I have also looked at the current open source CoreCLR code but have not been able to find out what the issue might be.
CoreCLR: https://github.com/dotnet/coreclr/blob/master/src/vm/reflectioninvocation.cpp
The exception is thrown on the lines: 2435, 2444, 2447
Does anyone have an idea on how to resolve this exception?
The CLR is unknown to the type parameter in WithGeneric
class, which it needs to know to create the call graph.
Doing this will solve the issue:
class Program
{
static void Main(string[] args)
{
new WithoutGeneric().GoExecute();
new WithGeneric<string>().GoExecute();
}
}
public class WithoutGeneric
{
public void GoExecute()
{
//Works fine
StaticMethods.PrepareThisFunc1(() => "Test");
}
}
public class WithGeneric<T>
{
public void GoExecute()
{
//Works fine
StaticMethods.PrepareThisFunc2(() => "Test");
}
}
public static class StaticMethods
{
public static void PrepareThisFunc1(Func<string> theFunc)
{
RuntimeHelpers.PrepareMethod(theFunc.Method.MethodHandle);
}
public static void PrepareThisFunc2(Func<string> theFunc)
{
RuntimeHelpers.PrepareMethod(theFunc.Method.MethodHandle, new[] { typeof(string).TypeHandle });
}
}