I have a private static generic method I want to call using reflection, but really I want to 'bundle' it inside of another method. C# 7.0 supports local functions so this is definitely possible.
You would say "why don't you just call it directly?" but I'm using it to get the ability to use an object and System.Type in a strongly typed manner so I need to call it dynamically. This code already works if I have it as it's own private static generic method.
private static void HandleResponse(object data, Type asType)
{
var application = typeof(Program);
application
.GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(asType)
.Invoke(null, new object[] { data });
}
public static void UseAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
};
The above code works. If I pass in:
data: new TestObject(),
type: typeof(TestObject)
I'll actually have a TestObject inside UseAs.
So, I wanted to put this all in a single method, like so:
private static void HandleResponse(object data, Type asType)
{
void useAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
};
var application = typeof(Program);
application
.GetMethod(nameof(UseAs), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(asType)
.Invoke(null, new object[] { data });
}
Unfortunately, the GetMethod code no longer works. I had heard that on compile time the compiler converts any local functions to static methods so I popped down to the immediate window and ran:
application.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
... And, I actually DO see this response:
{System.Reflection.MethodInfo[3]}
[0]: {Void Main(System.String[])}
[1]: {Void HandleResponse(System.Object, System.Type)}
[2]: {Void <HandleResponse>g__useAs1_0[T](T)}
It's the last method on the list. Does anyone have any idea how you would access a method like that in a reasonable way?
Thank you!
edit:
I can indeed use UseAs as an ordinary private static method. It's just not going to be used anywhere else so I wanted to "package" it all up inside one method.
In addition, this was really supposed to be a question about finding local functions in general and there doesn't seem to be a question about it anywhere else on StackOverflow. I find it hard to believe that at SOME POINT someone won't, at the very least, be curious about to do so.
I was hesitant to provide any code in the first place because I'm just tinkering with an idea, but the actual goal I'm trying to accomplish is secondary to the question altogether.
“Invoking” refers to calling a method. In winforms Control. Invoke is used to call a method on the UI thread — without it you can cause an exception by updating the UI from another thread. And so if InvokeRequires returns true it means that you are not running in the UI thread and should use Control.
Calling a generic method with a type parameter known only at runtime can be greatly simplified by using a dynamic type instead of the reflection API. To use this technique the type must be known from the actual object (not just an instance of the Type class).
Okay, I've got a solution. But it's really horrible. It involves creating a delegate from your method with a specific type, then using that to find the generic method, then constructing another specific method and invoking it.
So we go from UseAs<int>
to UseAs<T>
to UseAs<the-type-we-want>
.
It could go horribly wrong in many ways, but it works for the very limited sample I've tested:
// DISCLAIMER: THIS CODE IS FAIRLY HACKY, AND MAY WELL FAIL IN WEIRD
// SITUATIONS. USE WITH EXTREME CAUTION AND LOTS OF TESTS!
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
HandleResponse("foo", typeof(string));
}
static void HandleResponse(object data, Type type)
{
string local = "This was a local variable";
void UseAs<T>(T obj)
{
Console.WriteLine($"Object is now a: {typeof(T)}:");
// Proof that we're capturing the target too
Console.WriteLine($"Local was {local}");
}
InvokeHelper(UseAs, data, type);
}
// This could be in any class you want
static void InvokeHelper(Action<int> int32Action, object data, Type type)
{
// You probably want to validate that it really is a generic method...
var method = int32Action.Method;
var genericMethod = method.GetGenericMethodDefinition();
var concreteMethod = genericMethod.MakeGenericMethod(new[] { type });
concreteMethod.Invoke(int32Action.Target, new[] { data });
}
}
Calling a local function with reflection is like looking for trouble. The name isn't "fixed". It changes based on how many other local functions there are in the same class... So if you modify another method you could change the name of the local function you are interested in.
You can take a look at this TryRoslyn.
There are three classes, Class1
, Class2
and Class3
. They all have a method M
that internally has a local function Test
. Class1
and Class2
are identical to the last character. The local method is then compiled to a method named <M>g__Test0_0()
. Class3
introduces before the M
method another method, Filler
, with another local function (Foo
) that is then compiled to <Filler>g__Foo0_0
. In this case the local method of M
is named <M>g__Test1_0()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With