Reflection.Emit: How to convert MethodBuilder to RuntimeMethodInfo reliably? - c#

After generating a type dynamically and calling TypeBuilder.CreateType, I want to create a delegate that points to a method in the new type. But if I use code like
loadedType = typeBuilder.CreateType();
myDelegate = (MyDelegate)Delegate.CreateDelegate(
typeof(MyDelegate), methodBuilder);
Reusing the methodBuilder as a methodInfo, I get the exception "MethodInfo must be a RuntimeMethodInfo". Now normally I can re-acquire the MethodInfo with
MethodInfo mi = loadedType.GetMethod(methodBuilder.Name);
myDelegate = (MyDelegate)Delegate.CreateDelegate(typeof(MyDelegate), mi);
But my class may contain several overloaded methods with the same name. How do I make sure I get the right one? Do methods have some persistent identifier I could look up in loadedType?
Update: okay, here's what I'm using to re-acquire the MethodInfo. I just wish I could be sure it works in all cases.
private static MethodInfo ReacquireMethod(Type type, MethodInfo method)
{
BindingFlags flags = BindingFlags.DeclaredOnly;
flags |= (method.IsPublic ? BindingFlags.Public : BindingFlags.NonPublic);
flags |= (method.IsStatic ? BindingFlags.Static : BindingFlags.Instance);
MethodInfo m = type.GetMethod(method.Name, flags, null,
ParameterTypes(method), null);
Debug.Assert(m != null);
return m;
}

As far as I'm aware there's no persistent shared identifier.
Overloads of a method are distinguished by their parameter lists, so my guess is that you'll need to call GetParameters on methodBuilder and then translate the returned ParameterInfo[] array into a Type[] array to pass to the the appropriate GetMethod overload:
MethodInfo mi = loadedType.GetMethod(
methodBuilder.Name,
methodBuilder.GetParameters().Select(p => p.ParameterType).ToArray());

You need to use the GetMethod overload that takes an array of types. Method overloads are selected for binding by the parameters passed to them. Since you are not actually calling a method when getting their metadata via reflection, you have to provide an ordered array of Type objects to get the right MethodInfo object:
Type.GetMethod Method (String, Type[])
Used like:
MethodInfo mi = loadedType.GetMethod(
methodBuilder.Name,
new[]
{
typeof(string),
typeof(int)
}
);
Assiming methodBuilder.Name is "MyMethod", that would bind the following:
public <returnType> MyMethod(string param1, int param2);
Remember that the return type is not a part of a method signature, only the method name and its parameter types are.

Related

Method created during runtime changes the parameter order of another method call depending on how it is run

I implement interfaces during runtime using Reflection.Emit and create their defined methods.
Example definition of a method in the interface:
IFoo DoSomething(IBar bar, string name);
To create the methods I do the following:
var args = methodInfo.GetParameters();
MethodBuilder methodBuilder = typeBuilder.DefineMethod(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual,
methodInfo.ReturnType, (from arg in args select arg.ParameterType).ToArray());
typeBuilder.DefineMethodOverride(methodBuilder, methodInfo);
var generator = methodBuilder.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldfld, fieldBuilder);
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
if (args.Any())
{
generator.Emit(OpCodes.Ldc_I4_S, args.Length);
generator.Emit(OpCodes.Newarr, typeof(object));
for (int i = 0; i < args.Length; i++)
{
generator.Emit(OpCodes.Dup);
generator.Emit(OpCodes.Ldc_I4_S, i);
generator.Emit(OpCodes.Ldarg_S, i + 1);
generator.Emit(OpCodes.Stelem_Ref);
}
}
else
{
generator.Emit(OpCodes.Ldc_I4_0);
}
generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Ret);
This generates me for example my DoSomething(IBar bar, string name) method of the interface.
All generated methods call the method Test() of ISomeType.
This is the method Test():
public object Test(Type type, object[] arguments)
{
//do something
}
When I run my application and call the DoSomething() method, the parameters are passed the wrong way (object[] first and Type second) to Test(), which obviously leads to an exception.
But when I run a unit test and call the DoSomething() method, the parameters are passed correctly (Type first and object[] second) to Test().
Why is there a difference in which order the parameters are passed to the Test() method depending on how it is run?
So with a lot of help of Marc Gravell in the comments I found out what to do to get this problem fixed.
First of all I had an error in the way I created my methods.
The case when a method has no arguments has to be changed from
generator.Emit(OpCodes.Ldc_I4_0);
that pushes just a 0 as Int32 onto the stack, to
MethodInfo emptyArray = typeof(Array).GetMethod(nameof(Array.Empty))?.MakeGenericMethod(typeof(object));
generator.EmitCall(OpCodes.Call, emptyArray, null);
which really pushes an empty array to the stack that will get passed as an argument to the Test() function.
To fix the problem with the wrong parameter order there are two possible solutions in my specific case:
(1) More changes to the generated IL code:
Really push the return type of the method as a Type and not a RuntimeTypeHandle:
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
MethodInfo getTypeFromHandle = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
generator.EmitCall(OpCodes.Call, getTypeFromHandle, null);
Cast the return type of Test() to the wanted return type of the method before returning:
generator.EmitCall(OpCodes.Callvirt, typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] {typeof(Type), typeof(object[])}), null);
generator.Emit(OpCodes.Castclass, methodInfo.ReturnType);
generator.Emit(OpCodes.Ret);
(2) Convert Test() method to a generic method:
In case you have parameters as a Type you can convert your method to a generic method:
public T Test<T>(object[] arguments)
{
//do something
}
If you decide to do so, you will have to change some of the IL code generation as well:
Don't push return type of the method to the stack:
Remove this line:
generator.Emit(OpCodes.Ldtoken, methodInfo.ReturnType);
Change the call of Test():
MethodInfo test = typeof(ISomeType).GetMethod(nameof(ISomeType.Test), new[] { typeof(object[]) }).MakeGenericMethod(methodInfo.ReturnType);
generator.EmitCall(OpCodes.Callvirt, test, null);
generator.Emit(OpCodes.Ret);
You also don't have to cast the return value in this case because your method Test() already returns the correct type with T.
With any of those two solutions the Test() method gets call with the correct parameter order regardless of how it is called (application or unit test).
Another tip of Marc Gravell was to use Sigil to get clear error messages when your generated IL code is invalid. And no matter which of the two solutions above you are choosing I can only recommend to do so.

Creating a Matrix<T>.Build.Dense() using Expression.Call

I want to return an Expression.Call that creates a dense MathNet Matrix.
This is the Matrix I want:
Matrix<ContentType>.Build.Dense(Rows,Columns)
ContentType will be int, double or Complex.
But I want to create it using Expression.Call.
Here's my current code:
Expression.Call(
typeof(Matrix<>)
.MakeGenericType(ContentType)
.GetProperty("Build")
.GetMethod("Dense", new[] {typeof(int), typeof(int)}),
Expression.Constant(Rows), Expression.Constant(Columns));
This however results in a build error:
[CS1955] Non-invocable member 'PropertyInfo.GetMethod' cannot be used like a method.
What am I doing wrong?
There is GetMethod property on PropertyInfo type, which returns property getter method. You are trying to use this property as a method (invoke it) - hence the compiler error. Instead you should do it like this:
// first get Build static field (it's not a property by the way)
var buildProp = typeof(Matrix<>).MakeGenericType(ContentType)
.GetField("Build", BindingFlags.Public | BindingFlags.Static);
// then get Dense method reference
var dense = typeof(MatrixBuilder<>).MakeGenericType(ContentType)
.GetMethod("Dense", new[] { typeof(int), typeof(int) });
// now construct expression call
var call = Expression.Call(
Expression.Field(null /* because static */, buildProp),
dense,
Expression.Constant(Rows),
Expression.Constant(Columns));

Query Entity Framework for list of dynamic type

When querying entity framework for all objects of a given type, you'd do something like this:
List<MyDesiredObjects> = _myContext.MyDesiredObjects.ToList();
But is there a way one can create a function to query EF to find all objects of a given type if the type is variable? I've created this function, which at least compiles:
private List<TEntity> GetFromEF<TEntity>() where TEntity : class
{
MyDBEntities context = new UnityDBEntities(_entityConnection);
IObjectContextAdapter adapter = (IObjectContextAdapter)context;
System.Data.Objects.ObjectContext oContext = adapter.ObjectContext;
return oContext.CreateObjectSet<TEntity>().ToList();
}
But I'm struggling to see how I can call it, never mind actually extract data from it. Using reflection, like this (ucm.MappingType is a Type):
MethodInfo method = typeof(BaseXmlReader).GetMethod("GetFromEF");
MethodInfo gMethod = method.MakeGenericMethod( ucm.MappingType.GetType() );
var meh = gMethod.Invoke(null, null);
Fails with object reference not set to instance of object. And I've not even got to paging my list yet.
Any other way to approach this problem?
To address your issue of having a null MethodInfo method:
GetMethod will only search for public members. You can change your method to public, or include nonpublic methods like this:
MethodInfo method = typeof(BaseXmlReader)
.GetMethod("GetFromEF",
BindingFlags.Instance | BindingFlags.NonPublic);
Also, I'm assuming that your GetFromEF method is on a class called BaseXmlReader? If not, that needs to be replaced with the class name.
Then you can invoke like this:
BaseXmlReader instance = new BaseXmlReader();
MethodInfo method = typeof(BaseXmlReader)
.GetMethod("GetFromEF",
BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo gMethod = method.MakeGenericMethod(typeof(ucm.MappingType));
var result = gMethod.Invoke(instance, null);
or like this if being called from inside itself:
MethodInfo method = typeof(BaseXmlReader)
.GetMethod("GetFromEF",
BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo gMethod = method.MakeGenericMethod(typeof(ucm.MappingType));
var result = gMethod.Invoke(this, null);

reflection calling 2 methods

I'm trying to do something like this but with reflection:
var ss1= method1(param).method2();
I know how to invoke the first method but I don't know how to invoke the second one.
method 1 returns an object
method 2 is the GetAwaiter method(in my particular case)
MethodInfo mi = typeof(type).GetMethod("method1");
Type tt = typeof(type);
ParameterInfo[] param = mi.GetParameters();
object[] param = new object[] { //some code };
object mm= mi.Invoke(this, param);
MethodInfo mi2 = typeof(type).GetMethod("GetAwaiter");
var ss1= mi2.Invoke(mm,null);
on the last line i get an error: "object does not match target type"
Your code is more or less correct, but that exception is telling you that mm is not what you think it is. The type returned by your method1 is not the same as the type you have specified in the .GetMethod("GetAwaiter") line.
A safer bet would be to replace typeof(type) with mm.GetType().GetMethod("GetAwaiter"). I suspect that will actually return null, since mm.GetType() doesn't seem to be what you expect, but without seeing more code (in particular, what 'type' is and where the "method1" and "GetAwaiter" methods are actually declared) it's hard to give you much more guidance.
There is an error on this line
MethodInfo mi2 = typeof(type).GetMethod("GetAwaiter");
mm object has a different type, so you get an exception. Change the line to
MethodInfo mi2 = mm.GetType().GetMethod("GetAwaiter");
as Tim suggests.
It's better to use the Type.InvokeMember method since it's shorter and helps avoid errors like this. For example:
object mResult = this.GetType ().InvokeMember ("method1",
BindingFlags.InvokeMethod | BindingFlags.Instance, null, this,
new object[] { /* parameters here */ });
object awaiter = mResult.GetType ().InvokeMember ("GetAwaiter",
BindingFlags.InvokeMethod | BindingFlags.Instance, null, mResult, null);

Using reflection to find DynamicMethods

I'd like to somehow find all DynamicMethods in my current context, consider that I have the following method and delegate:
public delegate double DivideInvoker(int a, int b);
public DivideInvoker CreateInvoker()
{
Type[] methodArguments = {
typeof(int),
typeof(int)
};
DynamicMethod division = new DynamicMethod(
"Division",
typeof(double),
methodArguments,
typeof(MyMethodCreator));
ILGenerator il = division.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Div);
il.Emit(OpCodes.Ret);
var divideIt = (DivideInvoker)division.CreateDelegate(typeof(DivideInvoker));
return divideIt;
}
If I were to do: var divideIt = CreateInvoker();, can I somehow use reflection to find the dynamic method method?
According to MSDN the above dynamic method will be static and once it's not used anymore it will be disposed by the GC, I am just using this to play around with reflection.
I've tried getting all types in the executing assembly and listing all the methods on them, but I can't find anything on the DynamicMethod.
Any ideas?
DivideInvoker invoker = CreateInvoker();
MethodInfo method = invoker.Method;
// method will contain the MethodInfo of the Division dynamic method
// so you could use reflection on it

Categories

Resources