You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 07:57:31