如何创建适配含可选参数方法的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); }
工作原理
- 动态生成的委托类型完全复刻原方法的参数签名,包括每个参数的可选属性与默认值元数据;
- 当调用该委托的
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
相关产品推荐
相关产品推荐

