如何重构C#函数以降低SonarCube检测的认知复杂度?
重构方案:将认知复杂度降至15以下
把重复逻辑、独立职责的代码拆成单一功能的小方法,同时减少嵌套层级,就能快速降低认知复杂度。以下是具体步骤和重构后的代码:
1. 抽通用方法:根据ID/Guid获取产品
原代码里有两次几乎一致的产品查询逻辑(获取Tile和Edge),抽成泛型方法消除重复分支:
private T GetProductByIdOrGuid<T>(string id) where T : class { if (Guid.TryParse(id, out var guid)) { return ProductUtilities.GetProduct<T>(guid); } if (int.TryParse(id, out var intId)) { return ProductUtilities.GetProduct<T>(intId); } return null; }
2. 抽参数验证方法
把开头的ProductId合法性校验抽出来,提前返回错误,减少主方法的嵌套:
private IActionResult ValidateProductId(string productId) { if (string.IsNullOrWhiteSpace(productId)) { return new ObjectResult(new { Error = "You need to specify a product id" }) { StatusCode = (int)HttpStatusCode.InternalServerError }; } return null; }
3. 抽SizeId解析方法
把sizeId的拆分、数值解析逻辑独立出来,处理格式异常:
private (double? Length, double? Width) ParseSizeId(string sizeId) { if (string.IsNullOrWhiteSpace(sizeId)) { return (null, null); } var split = sizeId.Split('-'); if (split.Length != 2) { return (null, null); } if (double.TryParse(split[0], out var length) && double.TryParse(split[1], out var width)) { return (length, width); } return (null, null); }
4. 抽变体筛选逻辑
把Imperial/Metric两种单位下的维度筛选条件拆成独立方法,消除主方法的分支嵌套:
private IQueryable<RockstarProductTileVariant> FilterVariantsByDimensions(IQueryable<RockstarProductTileVariant> variants, double? length, double? width, double? thickness, bool isImperial) { if (isImperial) { return variants .Where(x => length == null || x.Rockstar_RockstarProductTileVariant_TileLengthInches == length || x.Rockstar_RockstarProductTileVariant_TileLengthFeet == length) .Where(x => width == null || x.Rockstar_RockstarProductTileVariant_TileWidthInches == width || x.Rockstar_RockstarProductTileVariant_TileWidthFeet == width) .Where(x => thickness == null || x.Rockstar_RockstarProductTileVariant_TileThicknessInches == thickness || x.Rockstar_RockstarProductTileVariant_TileThicknessFeet == thickness); } else { return variants .Where(x => length == null || x.Rockstar_RockstarProductTileVariant_TileLengthInches == length) .Where(x => width == null || x.Rockstar_RockstarProductTileVariant_TileWidthInches == width) .Where(x => thickness == null || x.Rockstar_RockstarProductTileVariant_TileThicknessInches == thickness); } }
5. 抽结果构建方法
把获取Edge、组装返回对象的逻辑独立出来,简化主方法流程:
private IActionResult BuildTileViewerResult(RockstarProductTile product, RockstarProductTileVariant variant, string edgeId) { var commerceService = new CommerceService(); var tileViewer = commerceService.GetRelatedEntries(variant, CommerceAssociationGroups.TileViewer).FirstOrDefault() as RockstarTileViewer; var images = tileViewer?.Rockstar_TileViewer_Images?.Items.Select(x => _urlUtilities.GetRelativeUrl(x.ContentLink)).ToList(); var edge = GetProductByIdOrGuid<RockstarProductEdge>(edgeId); var label = edge?.Rockstar_RockstarProductEdge_EdgeName?.SupAndSubOnly() ?? product.DisplayName; return images != null ? new JsonResult(new TileViewerDataObject { Label = label, Images = images }) : new JsonResult(new TileViewerDataObject { Label = label }); }
重构后的主方法
现在主方法流程线性、逻辑清晰,嵌套层级大幅减少,认知复杂度会降到15以下:
[HttpGet] public IActionResult GetTileViewer(string productId = "", string edgeId = "", string sizeId = "", double? thickness = null) { var validationResult = ValidateProductId(productId); if (validationResult != null) { return validationResult; } var commerceService = new CommerceService(); var isImperial = commerceService.IsImperialMeasure(); var product = GetProductByIdOrGuid<RockstarProductTile>(productId); if (product == null) { return new JsonResult(new TileViewerDataObject()); } try { var (length, width) = ParseSizeId(sizeId); var variants = product.Variants .Where(x => string.IsNullOrWhiteSpace(edgeId) || commerceService.GetRelatedEntries(x, CommerceAssociationGroups.Edge) .Any(y => y.ContentLink.ID.ToString() == edgeId || y.ContentGuid.ToString() == edgeId)); variants = FilterVariantsByDimensions(variants, length, width, thickness, isImperial); var variant = variants.FirstOrDefault(); if (variant != null) { return BuildTileViewerResult(product, variant, edgeId); } } catch (Exception ex) { _logger.LogError(ex, "Error during get tile viewer: {Message}", ex.Message); } return new JsonResult(new TileViewerDataObject()); }
复杂度降低的核心原因
- 消除了重复的分支逻辑(两次解析ID/Guid的代码)
- 把嵌套的if分支拆到独立方法中,大幅减少主方法的嵌套层级
- 每个小方法只负责单一职责,分支和判断被隔离,主方法流程更线性
- 移除了冗余的变量声明和临时变量,代码更简洁
内容的提问来源于stack exchange,提问作者cowefe
相关产品推荐
相关产品推荐

