C
C#3mo ago
Rettoph

Generically creating a delegate from a MethodInfo with parameter upcasting

I am trying to create a delegate of type TDelegate that allows me to invoke a MethodInfo instance with some upcasted parameters. A simple example of what i want is as follows:
using System.Reflection;

MethodInfo someFunctionMethodInfo = typeof(MyTestClass).GetMethod(nameof(MyTestClass.SomeFunction), BindingFlags.Static | BindingFlags.Public)!;

// Note, SomeFunction is an Action<object> and i want an Action<int>
// System.ArgumentException: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
var customDelegateUpcasting = DelegateHelper.CreateDelegateWithCasting<Action<int>>(someFunctionMethodInfo);
customDelegateUpcasting(10);

// Desired output:
// SomeFunction invoked with parameter: 10, Type: System.Int32

Console.ReadLine();

class DelegateHelper
{
public static TDelegate CreateDelegateWithCasting<TDelegate>(MethodInfo method)
where TDelegate : Delegate
{
// Currently throws:
// System.ArgumentException: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
return method.CreateDelegate<TDelegate>();
}
}

class MyTestClass
{
public static void SomeFunction(object param)
{
Console.WriteLine($"{nameof(SomeFunction)} invoked with parameter: {param}, Type: {param.GetType()}");
}
}
using System.Reflection;

MethodInfo someFunctionMethodInfo = typeof(MyTestClass).GetMethod(nameof(MyTestClass.SomeFunction), BindingFlags.Static | BindingFlags.Public)!;

// Note, SomeFunction is an Action<object> and i want an Action<int>
// System.ArgumentException: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
var customDelegateUpcasting = DelegateHelper.CreateDelegateWithCasting<Action<int>>(someFunctionMethodInfo);
customDelegateUpcasting(10);

// Desired output:
// SomeFunction invoked with parameter: 10, Type: System.Int32

Console.ReadLine();

class DelegateHelper
{
public static TDelegate CreateDelegateWithCasting<TDelegate>(MethodInfo method)
where TDelegate : Delegate
{
// Currently throws:
// System.ArgumentException: 'Cannot bind to the target method because its signature is not compatible with that of the delegate type.'
return method.CreateDelegate<TDelegate>();
}
}

class MyTestClass
{
public static void SomeFunction(object param)
{
Console.WriteLine($"{nameof(SomeFunction)} invoked with parameter: {param}, Type: {param.GetType()}");
}
}
The tldr, i want to take method void SomeFunction(object param) and create a delegate Action<int>... generically In this example im just calling MethodInfo::CreateDelegate<TDelegate>() - which fails. This is fine, i expect and understand why this happens. I'm just looking for ideas to achieve what im looking for. traditionally, i might just wrap the MethodInfo invocation within a lambda and call it a day... but since TDelegate is a generic type i dont actually know the number of parameters until the CreateDelegateWithCasting helper method is called - which is where my problem lies.
1 Reply
Rettoph
RettophOP3mo ago
I have been pursuing a possible solution by creating a DynamicMethod - the proof of concept i have at the moment looks like so:
class DelegateHelper
{
public static TDelegate CreateDelegateWithCasting<TDelegate>(MethodInfo method)
where TDelegate : Delegate
{
MethodInfo outDelegateSignature = typeof(TDelegate).GetMethod("Invoke") ?? throw new NotImplementedException();

DynamicMethod dynamicMethod = new DynamicMethod(
"Test",
typeof(void),
outDelegateSignature.GetParameters().Select(x => x.ParameterType).ToArray(),
typeof(Program).Module);

ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

// Load arguments
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Box, outDelegateSignature.GetParameters().First().ParameterType);

ilGenerator.Emit(OpCodes.Call, method);
ilGenerator.Emit(OpCodes.Ret);

return dynamicMethod.CreateDelegate<TDelegate>();
}
}
class DelegateHelper
{
public static TDelegate CreateDelegateWithCasting<TDelegate>(MethodInfo method)
where TDelegate : Delegate
{
MethodInfo outDelegateSignature = typeof(TDelegate).GetMethod("Invoke") ?? throw new NotImplementedException();

DynamicMethod dynamicMethod = new DynamicMethod(
"Test",
typeof(void),
outDelegateSignature.GetParameters().Select(x => x.ParameterType).ToArray(),
typeof(Program).Module);

ILGenerator ilGenerator = dynamicMethod.GetILGenerator();

// Load arguments
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Box, outDelegateSignature.GetParameters().First().ParameterType);

ilGenerator.Emit(OpCodes.Call, method);
ilGenerator.Emit(OpCodes.Ret);

return dynamicMethod.CreateDelegate<TDelegate>();
}
}
But... im already starting to see how much work its going to take to achieve my goal. Is this already a solved problem? I feel like i cant be the first to want this - is there a better solution than the IL & reflection madness ive embarked on? Perhaps a usefil library that already did the hard work for me? Any ideas would be hugely appreciated. thanks!
Want results from more Discord servers?
Add your server