Unity中CreateDelegate绑定首参后Invoke调用参数异常问题
你遇到的参数错乱问题,核心是误用了Delegate.CreateDelegate的重载。你调用的CreateDelegate(Type, object, MethodInfo)这个重载,设计初衷是给实例方法绑定this指针的:传入的object会作为实例方法的this,而不是方法的第一个参数。但你的Method1和Method2都是静态方法,静态方法没有this指针,所以这个重载根本不会帮你把mt1arg1/mt2arg1绑定为方法的第一个参数,反而会打乱整个参数传递逻辑:
- 对
Method1来说,调用Invoke("xyz")时,程序会错误地把"xyz"当成第一个参数(char类型),而你传入的'c'完全没被用上,乱码就是字符串强制转char时的错误结果。 - 对
Method2来说,传入的AuxSegment会被粗暴拆分:取它的p1作为第一个参数(Vector2),剩下的内存区域乱凑成第二个参数,导致输出完全错位。
而DynamicInvoke之所以能正常工作,是因为它会在运行时动态解析参数列表,相当于绕过了编译时的类型检查,“碰巧”匹配上了方法的参数顺序,但这种方式性能极差,不适合生产环境。
DynamicInvoke) 下面给你几种高效且类型安全的解决方式,按推荐程度排序:
方案1:Lambda闭包(最简单直观)
直接用Lambda表达式捕获第一个参数,生成符合TDelegate<T, T2>的委托。这种方式是编译时类型检查,性能和直接调用方法完全一致,代码也最简洁:
// 替换原来的del1和del2创建代码 var del1 = new TDelegate<bool, string>(arg2 => Method1(mt1arg1, arg2)); var del2 = new TDelegate<bool, AuxSegment>(arg2 => Method2(mt2arg1, arg2));
修改后再调用Invoke,输出就会和DynamicInvoke完全一致,参数传递准确无误。
方案2:表达式树编译(适合反射场景)
如果你的方法是通过反射动态获取的(而非直接引用),可以用System.Linq.Expressions构建表达式树,编译成目标委托。这种方式性能接近直接调用,远高于DynamicInvoke:
using System.Linq.Expressions; // 绑定Method1 var method1Info = typeof(YourClassName).GetMethod(nameof(Method1)); var arg2Param = Expression.Parameter(typeof(string), "arg2"); var arg1Const = Expression.Constant(mt1arg1); var callExpr = Expression.Call(method1Info, arg1Const, arg2Param); var del1 = Expression.Lambda<TDelegate<bool, string>>(callExpr, arg2Param).Compile(); // 绑定Method2 var method2Info = typeof(YourClassName).GetMethod(nameof(Method2)); var arg2Param2 = Expression.Parameter(typeof(AuxSegment), "arg2"); var arg1Const2 = Expression.Constant(mt2arg1); var callExpr2 = Expression.Call(method2Info, arg1Const2, arg2Param2); var del2 = Expression.Lambda<TDelegate<bool, AuxSegment>>(callExpr2, arg2Param2).Compile();
注意把YourClassName替换成你实际的类名。
方案3:封装泛型工具类(复用场景)
如果需要多次绑定不同方法的第一个参数,可以封装一个泛型工具类,复用绑定逻辑:
public static class DelegateBinder { public static TTargetDelegate BindFirst<TTargetDelegate, T1, T2, TResult>( Func<T1, T2, TResult> method, T1 firstArg) where TTargetDelegate : Delegate { return (TTargetDelegate)(Delegate)new Func<T2, TResult>(arg2 => method(firstArg, arg2)); } } // 使用示例 var del1 = DelegateBinder.BindFirst<TDelegate<bool, string>, char, string, bool>(Method1, mt1arg1); var del2 = DelegateBinder.BindFirst<TDelegate<bool, AuxSegment>, Vector2, AuxSegment, bool>(Method2, mt2arg1);
这种方式可以让你在多个地方快速复用绑定逻辑,保持代码整洁。
内容的提问来源于stack exchange,提问作者Alberto

