反射获取Controller方法返回类型泛型参数时Enumerable类型结果异常求助
解决反射获取嵌套泛型返回类型名称的问题
我一眼就看出问题出在你那串超长的ReturnType拼接代码里——括号配对错误导致Linq Select的迭代器类型被当成了泛型参数名称输出!咱们一步步拆解:
问题根源
你在处理第二层嵌套泛型(比如List<string>里的string,或者Dictionary<string,string>里的键值类型)时,错误地把Select(c => ...) + ">"直接传给了String.Join。因为Select返回的是IEnumerable<string>,直接和字符串">"拼接会触发默认的ToString(),返回的是迭代器的类型名称(也就是你看到的System.Linq.Enumerable+SelectArrayIterator2[System.Type,System.String]`),而不是你想要的实际类型名称列表。
解决方案:抽成递归方法
与其写一串容易出错的嵌套三元表达式,不如把泛型类型名称的格式化逻辑抽成一个递归方法,这样代码清晰,还能轻松处理任意多层的嵌套泛型。
步骤1:添加递归格式化方法
先写一个辅助方法,用来递归生成友好的类型名称:
private static string GetFriendlyTypeName(Type type) { // 非泛型类型直接返回名称 if (!type.IsGenericType) { return type.Name; } // 获取泛型类型的基础名称(比如从List`1得到List) var genericTypeName = type.Name.Substring(0, type.Name.IndexOf('`')); // 递归处理每个泛型参数 var genericArgs = string.Join(",", type.GetGenericArguments().Select(GetFriendlyTypeName)); // 拼接成完整的泛型类型名称 return $"{genericTypeName}<{genericArgs}>"; }
步骤2:修改你的查询代码
把原来超长的ReturnType替换成调用这个方法的逻辑,代码瞬间清爽:
Assembly asm = Assembly.GetExecutingAssembly(); var controllerMethodlist = asm.GetTypes() .Where(type => typeof(ControllerBase).IsAssignableFrom(type)) .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) .Where(m => !m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any()) .Select(x => new { Controller = x.DeclaringType.Name, Action = x.Name + "(" + string.Join(",", x.GetParameters().Select(p => p.Name)) + ")", // 调用辅助方法生成友好的返回类型名称 ReturnType = GetFriendlyTypeName(x.ReturnType), Attributes = string.Join(",", x.GetCustomAttributes().Select(a => { var attrType = a.GetType(); var attrName = attrType.Name.Replace("Attribute", ""); string attrValue = "*"; if (attrType == typeof(HttpGetAttribute)) attrValue = ((HttpGetAttribute)a).Template; else if (attrType == typeof(HttpPostAttribute)) attrValue = ((HttpPostAttribute)a).Template; else if (attrType == typeof(HttpDeleteAttribute)) attrValue = ((HttpDeleteAttribute)a).Template; else if (attrType == typeof(HttpPutAttribute)) attrValue = ((HttpPutAttribute)a).Template; else if (attrType == typeof(CrudControllerAuthAttribute)) attrValue = ((CrudControllerAuthAttribute)a).CrudAuth.ToString(); return $"{attrName}|{attrValue}"; })) }) .OrderBy(x => x.Controller).ThenBy(x => x.Action).ToList();
效果验证
现在再测试你的几个场景:
public async Task<ApiResult> SomeMethod();→ 输出Task<ApiResult>public async Task<List<string>> SomeMethod();→ 输出Task<List<string>>public async Task<Dictionary<string,string>> SomeMethod();→ 输出Task<Dictionary<string,string>>public async Task<ApiResultT<Dictionary<string,string>>> GetCsvDictionaryForTemplate(CsvBase64)→ 输出Task<ApiResultT<Dictionary<string,string>>>
完美符合你的预期!
内容的提问来源于stack exchange,提问作者Piercarlo
相关产品推荐
相关产品推荐

