C
C#8mo ago
Kaihyo

❔ Building a delegate with a Type and a MethodInfo keeps throwing ArgumentException

Hi ! I'm trying to build a delegate from a Type and a MethodInfo. The type correspond to the method 1st argument's type. I have the following code :
public System.Delegate CreateRemoveMethod(System.Type elementType, System.Reflection.MethodInfo methodInfo)
{
if (methodInfo == null)
{
return null;
}

System.Reflection.ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != 1 && parameters[0].ParameterType != elementType)
{
return null;
}

var action = typeof(System.Action<>).MakeGenericType(elementType);
return methodInfo.CreateDelegate(action);
}
public System.Delegate CreateRemoveMethod(System.Type elementType, System.Reflection.MethodInfo methodInfo)
{
if (methodInfo == null)
{
return null;
}

System.Reflection.ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length != 1 && parameters[0].ParameterType != elementType)
{
return null;
}

var action = typeof(System.Action<>).MakeGenericType(elementType);
return methodInfo.CreateDelegate(action);
}
Unfortunately, the function throws an exception every time I call it, saying method arguments are incompatible. The signature of the method I'm using to test it is the following one : void LogInConsole(BaseClass item). I'm calling CreateRemoveMethod like this : CreateRemoveMethod(typeof(BaseClass), methodInfo), with methodInfo corresponding LogInConsole that I gathered through reflection. Would you have any insight on what I'm doing wrong here ? 🙂
13 Replies
arion
arion8mo ago
Everything looks fine, could you paste the full exception here? This is my test scenario of yours that i copied and everything works fine
using System.Reflection;

class Program
{
static void Main()
{
var methodInfos = typeof(Program).GetMethods().First(x => x.Name == nameof(Something));

var type = typeof(string);

var method = CreateMethod<string>(methodInfos);

if (method == null)
{
Console.WriteLine("Null");
return;
}

method("hello");

}


public static Action<T>? CreateMethod<T>(MethodInfo info)
{
if (info == null)
return null;

var parameters = info.GetParameters();
if (parameters.Length != 1 && parameters[0].ParameterType != typeof(T) || info.ReturnType != typeof(void))
{
return null;
}

// var act = info.CreateDelegate<Action<T>>();

// return act;
var actio = typeof(Action<>).MakeGenericType(typeof(T));
return (Action<T>)info.CreateDelegate(actio);
}


public static void Something(string eelse)
{
Console.WriteLine(eelse);
}
}
using System.Reflection;

class Program
{
static void Main()
{
var methodInfos = typeof(Program).GetMethods().First(x => x.Name == nameof(Something));

var type = typeof(string);

var method = CreateMethod<string>(methodInfos);

if (method == null)
{
Console.WriteLine("Null");
return;
}

method("hello");

}


public static Action<T>? CreateMethod<T>(MethodInfo info)
{
if (info == null)
return null;

var parameters = info.GetParameters();
if (parameters.Length != 1 && parameters[0].ParameterType != typeof(T) || info.ReturnType != typeof(void))
{
return null;
}

// var act = info.CreateDelegate<Action<T>>();

// return act;
var actio = typeof(Action<>).MakeGenericType(typeof(T));
return (Action<T>)info.CreateDelegate(actio);
}


public static void Something(string eelse)
{
Console.WriteLine(eelse);
}
}
The comments are things you can do to make it neater or more easy with less method calls. I've even tried a more complex type like Func<string> as the first parameter to test and that works too
Kaihyo
Kaihyo8mo ago
I didn't make the method generic because I won't know the element type in every scenario. But here is the full exception
ArgumentException: method arguments are incompatible
System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure, System.Boolean allowClosed) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Reflection.RuntimeMethodInfo.CreateDelegate (System.Type delegateType) (at <cbc72d4a9767498db39486e941a498e3>:0)
ArgumentException: method arguments are incompatible
System.Delegate.CreateDelegate (System.Type type, System.Object firstArgument, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure, System.Boolean allowClosed) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method, System.Boolean throwOnBindFailure) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Delegate.CreateDelegate (System.Type type, System.Reflection.MethodInfo method) (at <cbc72d4a9767498db39486e941a498e3>:0)
System.Reflection.RuntimeMethodInfo.CreateDelegate (System.Type delegateType) (at <cbc72d4a9767498db39486e941a498e3>:0)
arion
arion8mo ago
I've converted the method to be this instead to fit your situation more everything still runs fine, I've even created a makeshift scenario of class inheritance to act as your base class.
static void Main()
{
var methodInfos = typeof(Program).GetMethods().First(x => x.Name == nameof(Something));

var type = typeof(MyClass);

var method = CreateMethod(methodInfos, type);

if (method == null)
{
Console.WriteLine("Null");
return;
}

method.DynamicInvoke(new Inherited("Program"));

}

internal abstract class MyClass
{
private readonly string name;

public MyClass(string name)
{
this.name = name;
}

public virtual void SayHello() => Console.WriteLine("Hello there " + name);
}

public class Inherited : MyClass
{
public Inherited(string name) : base(name)
{
}

public override void SayHello()
{
base.SayHello();
Console.WriteLine("Welcome to the Class!");
}
}


public static Delegate? CreateMethod(MethodInfo info, Type firstParameter)
{
if (info == null)
return null;

var parameters = info.GetParameters();

if (parameters.Length != 1 && (parameters[0].ParameterType != firstParameter || info.ReturnType != typeof(void)))
return null;


var actio = typeof(Action<>).MakeGenericType(firstParameter);

return info.CreateDelegate(actio);
}


public static void Something(MyClass eelse)
{
eelse.SayHello();
}
static void Main()
{
var methodInfos = typeof(Program).GetMethods().First(x => x.Name == nameof(Something));

var type = typeof(MyClass);

var method = CreateMethod(methodInfos, type);

if (method == null)
{
Console.WriteLine("Null");
return;
}

method.DynamicInvoke(new Inherited("Program"));

}

internal abstract class MyClass
{
private readonly string name;

public MyClass(string name)
{
this.name = name;
}

public virtual void SayHello() => Console.WriteLine("Hello there " + name);
}

public class Inherited : MyClass
{
public Inherited(string name) : base(name)
{
}

public override void SayHello()
{
base.SayHello();
Console.WriteLine("Welcome to the Class!");
}
}


public static Delegate? CreateMethod(MethodInfo info, Type firstParameter)
{
if (info == null)
return null;

var parameters = info.GetParameters();

if (parameters.Length != 1 && (parameters[0].ParameterType != firstParameter || info.ReturnType != typeof(void)))
return null;


var actio = typeof(Action<>).MakeGenericType(firstParameter);

return info.CreateDelegate(actio);
}


public static void Something(MyClass eelse)
{
eelse.SayHello();
}
By any chance are you executing that within the context of an Il2Cpp unity game? If you are passing through an Il2Cpp type that may cause some exceptions
Kaihyo
Kaihyo8mo ago
Yes, it's within unity for editor code I suspected it could be linked to this That's bothersome
arion
arion8mo ago
Is your unity game built to target il2cpp? or is it just normal mono?
Kaihyo
Kaihyo8mo ago
Well, since it's code to modify the editor behaviour, I'll suppose it depends on what unity is running on It's not like a game which I could decide if it's il2cpp or mono unfortunately 😦 But thanks to you I know that it's a unity issue now and apparently the method needs to be a static method
arion
arion8mo ago
hmm, if it wasnt a static, you'd need to provide an instance as the first parameter
Kaihyo
Kaihyo8mo ago
Yes
arion
arion8mo ago
then normal parameters following
Kaihyo
Kaihyo8mo ago
And I think that's what I was missing
arion
arion8mo ago
could very well be that
Kaihyo
Kaihyo8mo ago
That was the issue, i was missing the instance. Thanks for the help 🙂
Accord
Accord8mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.