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

基于Net6.0的泛型委托测量:包装GetDelegateForFunctionPointer

实现带耗时统计的泛型Native函数加载器

需求说明

基于现有泛型Native函数加载逻辑,实现一个包装器:返回与原委托签名完全一致的委托,内部自动测量原Native函数的调用耗时并输出日志。要求兼容net6.0(Microsoft.NET.Sdk.Web SDK),仅限Windows平台。

核心计时逻辑示例:

var sw = Stopwatch.StartNew();
var rtn = 原委托调用();
sw.Stop();
Console.WriteLine($"elapsed: {sw.Elapsed}");
return rtn;

修改后的完整实现

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.InteropServices;

public static class NativeFunctionLoader
{
    private static readonly ConcurrentDictionary<string, IntPtr> LoadedLibraries = new();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

    public static T LoadFunction<T>(string dllPath, string functionName)
        where T : Delegate
    {
        // 标准化路径
        if (!Path.IsPathFullyQualified(dllPath))
        {
            dllPath = Path.GetFullPath(dllPath);
        }

        // 加载或复用已加载的库
        IntPtr hModule = LoadedLibraries.GetOrAdd(
            dllPath,
            dll =>
            {
                IntPtr loaded = LoadLibrary(dll);
                if (loaded == IntPtr.Zero)
                {
                    throw new DllNotFoundException($"未找到指定路径的库:{dllPath}");
                }
                return loaded;
            }
        );

        // 获取函数地址
        IntPtr functionAddress = GetProcAddress(hModule, functionName);
        if (functionAddress == IntPtr.Zero)
        {
            throw new EntryPointNotFoundException($"在{dllPath}中未找到函数:{functionName}");
        }

        // 获取原始委托
        var originalDelegate = Marshal.GetDelegateForFunctionPointer<T>(functionAddress);

        // 生成带耗时统计的包装委托
        return CreateTimedWrapper(originalDelegate);
    }

    private static T CreateTimedWrapper<T>(T originalDelegate)
        where T : Delegate
    {
        Type delegateType = typeof(T);
        MethodInfo invokeMethod = delegateType.GetMethod("Invoke")!;
        
        // 定义参数:和原委托的Invoke方法参数一致
        ParameterExpression[] parameters = invokeMethod.GetParameters()
            .Select(p => Expression.Parameter(p.ParameterType, p.Name))
            .ToArray();

        // 调用原委托的表达式
        Expression invokeOriginal = Expression.Invoke(Expression.Constant(originalDelegate), parameters);

        // 构建计时逻辑的表达式
        var stopwatchVar = Expression.Variable(typeof(Stopwatch), "sw");
        BlockExpression block;

        // 区分有返回值和无返回值的委托
        if (invokeMethod.ReturnType == typeof(void))
        {
            block = Expression.Block(
                new[] { stopwatchVar },
                Expression.Assign(stopwatchVar, Expression.Call(typeof(Stopwatch), "StartNew", Type.EmptyTypes)),
                invokeOriginal,
                Expression.Call(stopwatchVar, nameof(Stopwatch.Stop), Type.EmptyTypes),
                Expression.Call(
                    typeof(Console), 
                    nameof(Console.WriteLine), 
                    Type.EmptyTypes,
                    Expression.Add(
                        Expression.Constant($"[{typeof(T).Name}] 调用耗时:"),
                        Expression.Property(stopwatchVar, nameof(Stopwatch.Elapsed))
                    )
                )
            );
        }
        else
        {
            var resultVar = Expression.Variable(invokeMethod.ReturnType, "result");
            block = Expression.Block(
                new[] { stopwatchVar, resultVar },
                Expression.Assign(stopwatchVar, Expression.Call(typeof(Stopwatch), "StartNew", Type.EmptyTypes)),
                Expression.Assign(resultVar, invokeOriginal),
                Expression.Call(stopwatchVar, nameof(Stopwatch.Stop), Type.EmptyTypes),
                Expression.Call(
                    typeof(Console), 
                    nameof(Console.WriteLine), 
                    Type.EmptyTypes,
                    Expression.Add(
                        Expression.Constant($"[{typeof(T).Name}] 调用耗时:"),
                        Expression.Property(stopwatchVar, nameof(Stopwatch.Elapsed))
                    )
                ),
                resultVar
            );
        }

        // 编译表达式为委托
        var lambda = Expression.Lambda<T>(block, parameters);
        return lambda.Compile();
    }
}

关键说明

  • 泛型约束增强:给LoadFunction<T>添加where T : Delegate约束,明确T必须是委托类型,避免无效调用。
  • 表达式树动态适配:通过表达式树自动适配任意签名的委托(带参数/无参数、有返回值/无返回值),生成的包装委托与原委托签名完全一致。
  • net6.0兼容性:使用的表达式树API、强类型Marshal.GetDelegateForFunctionPointer<T>均在net6.0中完全支持。
  • Windows平台限定:依赖kernel32.dll的LoadLibrary和GetProcAddress接口,仅适用于Windows系统。

使用示例

假设Native函数签名如下:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int AddNumbers(int a, int b);

加载并调用带计时的函数:

var addFunc = NativeFunctionLoader.LoadFunction<AddNumbers>("MyNativeLib.dll", "AddNumbers");
int result = addFunc(10, 20);
// 控制台会输出类似:[AddNumbers] 调用耗时:00:00:00.0001234

内容的提问来源于stack exchange,提问作者Marek Sebera

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 11:05:18