能否通过Type[]动态创建委托并实现泛型方法的类型推断?
能否通过Type[]创建委托实现泛型方法的类型推断?
你的原方案不可行,但可以通过反射调用泛型方法的方式实现动态传入delegateType的需求。
一、原方案的问题所在
原代码中CreateDelegate方法返回的是Delegate基类实例,调用Call<TDelegate>时,编译器会将TDelegate推断为Delegate而非你期望的具体委托类型(比如Func<int, int, int>)。而Marshal.GetDelegateForFunctionPointer要求TDelegate必须是具体的委托类型(不能是抽象的Delegate类),运行时会抛出ArgumentException异常。
另外原Call方法还有两处隐患:
- 未用
finally块确保FreeLibrary执行,会导致原生库句柄泄漏 - 直接用
function(args)调用委托是错误的,需要用DynamicInvoke传递参数数组
二、可行的实现方案
我们不需要创建无用的委托实例做类型推断,而是通过反射构造泛型方法,直接将动态创建的delegateType作为泛型参数传入Call方法。
修正后的Call方法
using System.ComponentModel; using System.Linq.Expressions; using System.Runtime.InteropServices; class NativeLibraryCaller { [DllImport("kernel32.dll", SetLastError = true)] private static extern nint LoadLibrary(string lpFileName); [DllImport("kernel32.dll", SetLastError = true)] private static extern nint GetProcAddress(nint hModule, string lpProcName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(nint hModule); public void Call<TDelegate>(string func, out object? returnValue, params object?[]? args) where TDelegate : Delegate { nint libHandle = IntPtr.Zero; returnValue = null; try { libHandle = LoadLibrary("library.dll"); if (libHandle == IntPtr.Zero) throw new Win32Exception(); nint procAddress = GetProcAddress(libHandle, func); if (procAddress == IntPtr.Zero) throw new Win32Exception(); TDelegate function = Marshal.GetDelegateForFunctionPointer<TDelegate>(procAddress); returnValue = function.DynamicInvoke(args); } finally { if (libHandle != IntPtr.Zero) FreeLibrary(libHandle); } } }
反射调用泛型方法的示例
var caller = new NativeLibraryCaller(); object? returnValue = null; // 创建对应原生函数签名的委托类型:int(int, int) // 注意:Expression.GetDelegateType的参数顺序是 [参数类型列表, 返回值类型] Type delegateType = Expression.GetDelegateType(typeof(int), typeof(int), typeof(int)); // 获取Call方法的泛型定义 MethodInfo callMethod = typeof(NativeLibraryCaller).GetMethod(nameof(NativeLibraryCaller.Call))!; // 构造带具体委托类型的泛型方法 MethodInfo genericCallMethod = callMethod.MakeGenericMethod(delegateType); // 准备调用参数(out参数需要用数组传递以接收返回值) object[] parameters = new object[] { "Add", returnValue, new object[] { 32, 67 } }; // 调用泛型方法 genericCallMethod.Invoke(caller, parameters); // 从参数数组中取出out参数的值 returnValue = parameters[1]; Console.WriteLine(returnValue); // 输出:99
三、补充说明
- 如果原生函数签名能匹配
Func/Action系列预定义委托,可以直接使用这些类型,避免动态创建委托类型 - 动态生成的委托类型无法在编译时被泛型推断捕获,反射是这类场景下最直接的解决方案
内容的提问来源于stack exchange,提问作者Jaime de la Rocha
相关产品推荐
相关产品推荐

