C#中如何优雅动态构建带可选参数的GET查询字符串
方案1:反射通用实现(最省心,改动最小)
直接通过反射遍历QueryClass的所有公共属性,自动过滤空值生成字典,不需要针对每个属性写硬编码逻辑:
private string BuildQuery(QueryClass query) { var queryDictionary = query.GetType() .GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) .Where(prop => prop.GetValue(query) is string value && !string.IsNullOrEmpty(value)) .ToDictionary( prop => prop.Name.ToLowerInvariant(), // 可根据需求调整参数名大小写规则 prop => (string)prop.GetValue(query) ); return new FormUrlEncodedContent(queryDictionary).ReadAsStringAsync().Result; }
提示:如果需要支持自定义参数名(比如属性名为
Offset,接口要求参数为current_offset),可以给属性加[JsonPropertyName("current_offset")]或自定义特性,反射时读取特性值作为字典的key即可。
方案2:原生封装字典(无反射开销,性能最优)
直接调整QueryClass的结构,内部封装字典,属性赋值时自动写入字典,完全避免运行时反射开销:
public class QueryClass : Dictionary<string, string> { public string Offset { get => TryGetValue("offset", out var val) ? val : null; set { if (!string.IsNullOrEmpty(value)) this["offset"] = value; else Remove("offset"); } } public string Limit { get => TryGetValue("limit", out var val) ? val : null; set { if (!string.IsNullOrEmpty(value)) this["limit"] = value; else Remove("limit"); } } public string Sort { get => TryGetValue("sort", out var val) ? val : null; set { if (!string.IsNullOrEmpty(value)) this["sort"] = value; else Remove("sort"); } } }
对应的BuildQuery方法可以简化到极致:
private string BuildQuery(QueryClass query) { return new FormUrlEncodedContent(query).ReadAsStringAsync().Result; }
这种方案虽然仍需要定义属性,但不需要额外维护if判断逻辑,属性赋值时自动处理空值过滤和字典写入,适合性能要求高的场景。
方案3:源生成器(兼顾性能和少写代码)
如果QueryClass属性特别多,又不想用反射也不想挨个写属性的字典逻辑,可以用C#源生成器在编译时自动生成转换逻辑,没有运行时开销,也不需要手动写硬编码。
额外优化建议
原代码中使用ReadAsStringAsync().Result属于同步等待异步方法,高并发场景下可能出现死锁,建议将BuildQuery改为异步方法:
private async Task<string> BuildQueryAsync(QueryClass query) { var queryDictionary = // 上述任意方案生成的字典 using var content = new FormUrlEncodedContent(queryDictionary); return await content.ReadAsStringAsync(); }
内容的提问来源于stack exchange,提问作者pittcrew
相关产品推荐
相关产品推荐

