运行时为方法创建委托失败求助(C#反射扩展方法问题)
解决Lambda参数数量不匹配的问题
你遇到的“lambda声明使用了错误数量的参数”问题,根源在于BuildMethodAccessor里创建表达式Lambda时,只传入了实例参数obj,但你的委托类型Func<object, string, object>需要两个参数(实例对象和string参数),参数列表不完整导致报错。
修正后的BuildMethodAccessor方法
public static Func<object, string, object> BuildMethodAccessor(MethodInfo method) { var obj = Expression.Parameter(typeof(object), "o"); var strParam = Expression.Parameter(typeof(string), "strParam"); // 转换实例到方法所属类型,转换参数到方法要求的参数类型 var instanceExpr = Expression.Convert(obj, method.DeclaringType); var paramExpr = Expression.Convert(strParam, method.GetParameters().First().ParameterType); // 构建方法调用表达式 var callExpr = Expression.Call(instanceExpr, method, paramExpr); // 转换返回值到object var resultExpr = Expression.Convert(callExpr, typeof(object)); // 关键:Lambda需要传入所有参数(obj和strParam) var expr = Expression.Lambda<Func<object, string, object>>(resultExpr, obj, strParam); return expr.Compile(); }
为什么之前的代码报错?
原来的代码里,Expression.Lambda的第二个参数只传了obj:
Expression.Lambda<Func<object, string, object>>(..., obj);
但Func<object, string, object>定义了两个输入参数,必须把obj和strParam都作为参数传入Lambda,这样表达式树才能正确匹配委托的参数数量。
完整的测试流程验证
你的调用代码不需要修改,修正后的BuildMethodAccessor可以正常工作。这里再贴一下完整的调用流程,确保端到端运行:
bmecatParser parser = new bmecatParser(); // 获取主类实例 var property = parser.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "BmecatContent"); var access = Extensions.BuildGetAccessor(property.GetGetMethod()); var resultBmecatContent = access(parser); // 获取包含目标方法的类实例 property = resultBmecatContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "Catalog"); access = Extensions.BuildGetAccessor(property.GetGetMethod()); var resultCatalog = access(resultBmecatContent); // 调用带string参数的方法 var method = resultCatalog.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance).Single(obj => obj.Name == "GetProductByInernationalPid"); var accessProd = Extensions.BuildMethodAccessor(method); var resultProduct = accessProd(resultCatalog, "4317784548366");
扩展建议
如果以后需要支持更多参数类型的方法,可以把BuildMethodAccessor改成通用版本,适配任意参数数量和类型:
public static Delegate BuildGenericMethodAccessor(MethodInfo method) { // 创建实例参数 var instanceParam = Expression.Parameter(typeof(object), "instance"); // 创建方法参数列表 var methodParams = method.GetParameters() .Select(p => Expression.Parameter(p.ParameterType, p.Name)) .ToList(); // 转换实例到方法所属类型 var instanceExpr = Expression.Convert(instanceParam, method.DeclaringType); // 构建方法调用表达式 var callExpr = Expression.Call(instanceExpr, method, methodParams); // 转换返回值到object(如果方法返回值是void,需要调整) var resultExpr = method.ReturnType == typeof(void) ? callExpr : Expression.Convert(callExpr, typeof(object)); // 合并参数:实例参数 + 方法参数 var allParams = new List<ParameterExpression> { instanceParam }; allParams.AddRange(methodParams); // 创建Lambda并编译 var lambda = Expression.Lambda(resultExpr, allParams); return lambda.Compile(); }
使用这个通用版本时,你需要根据方法的参数类型转换委托,比如对于GetProductByInernationalPid:
var accessProd = (Func<object, string, object>)BuildGenericMethodAccessor(method);
内容的提问来源于stack exchange,提问作者jake
相关产品推荐
相关产品推荐

