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

如何创建适配含可选参数方法的Delegate?

解决Delegate处理可选参数时DynamicInvoke报错问题

问题核心

你当前实现的CreateDelegate方法,在复制带可选参数的方法委托时,由于复用了传入委托的类型(如Action<T>这类泛型委托),而该类型不携带可选参数的元数据,导致调用DynamicInvoke传入Type.Missing时,无法识别并替换为参数默认值,抛出System.ArgumentException: Missing parameter does not have a default value异常。

解决方案

通过动态生成与原方法签名完全匹配(包含可选参数默认值元数据)的委托类型,再基于该类型创建委托,即可让DynamicInvoke正确处理Type.Missing。

实现代码

using System.Reflection;
using System.Reflection.Emit;

private Delegate CreateDelegate(Delegate method)
{
    var methodInfo = method.Method;
    var parameters = methodInfo.GetParameters();

    // 动态构建委托类型所在的临时程序集与模块
    var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
        new AssemblyName("DynamicDelegateAssembly"), 
        AssemblyBuilderAccess.Run);
    var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicDelegateModule");

    // 定义动态委托类型
    var delegateTypeBuilder = moduleBuilder.DefineType(
        $"Dynamic_{methodInfo.Name}_{Guid.NewGuid():N}",
        TypeAttributes.Public | TypeAttributes.Sealed,
        typeof(MulticastDelegate));

    // 定义委托的构造函数(必须符合委托类型的构造函数签名)
    var ctorBuilder = delegateTypeBuilder.DefineConstructor(
        MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public,
        CallingConventions.Standard,
        new[] { typeof(object), typeof(IntPtr) });
    ctorBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

    // 定义委托的Invoke方法,签名与原方法完全一致
    var invokeMethodBuilder = delegateTypeBuilder.DefineMethod(
        "Invoke",
        MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual,
        methodInfo.ReturnType,
        parameters.Select(p => p.ParameterType).ToArray());

    // 为参数设置可选属性与默认值(如果原方法参数有默认值)
    for (int i = 0; i < parameters.Length; i++)
    {
        var param = parameters[i];
        var paramBuilder = invokeMethodBuilder.DefineParameter(
            i + 1, 
            param.HasDefaultValue ? ParameterAttributes.Optional : ParameterAttributes.None, 
            param.Name);
        if (param.HasDefaultValue)
        {
            paramBuilder.SetConstant(param.DefaultValue);
        }
    }
    invokeMethodBuilder.SetImplementationFlags(MethodImplAttributes.Runtime | MethodImplAttributes.Managed);

    // 完成委托类型的创建
    var delegateType = delegateTypeBuilder.CreateType();

    // 基于动态生成的委托类型创建委托实例
    return Delegate.CreateDelegate(delegateType, method.Target, methodInfo);
}

工作原理

  1. 动态生成的委托类型完全复刻原方法的参数签名,包括每个参数的可选属性与默认值元数据;
  2. 当调用该委托的DynamicInvoke时,.NET运行时会自动识别Type.Missing,并替换为对应参数的默认值,避免异常。

测试示例

// 带可选参数的测试方法
public void TestMethod(int num = 10, string text = "default")
{
    Console.WriteLine($"Num: {num}, Text: {text}");
}

// 使用示例
var originalDelegate = new Action<int, string>(TestMethod);
var dynamicDelegate = CreateDelegate(originalDelegate);

// 传入Type.Missing,自动使用默认值
dynamicDelegate.DynamicInvoke(Type.Missing, Type.Missing); // 输出: Num: 10, Text: default
// 混合传入参数与Type.Missing
dynamicDelegate.DynamicInvoke(20, Type.Missing); // 输出: Num: 20, Text: default

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 22:07:48