CA1502:避免过度复杂

属性
规则 ID CA1502
标题 避免过度复杂
类别 可维护性
修复是中断修复还是非中断修复 非中断
默认阈值 25
在 .NET 9 中默认启用

原因

某方法具有过度的圈复杂度。

规则说明

圈复杂度衡量经过该方法的线性独立路径的数量,由条件分支的数量和复杂度决定。 圈复杂度较低通常表示方法易于理解、测试和维护。 圈复杂度通过方法的控制流图计算得出,公式如下:

圈复杂度 = 边缘数 - 节点数 + 1

节点表示逻辑分支点,边缘表示节点之间的线 。

当方法的圈复杂度大于 25 时,规则会报告冲突。 但可以配置阈值,还可以指定规则应分析的其他类型的符号。

可以在衡量托管代码的复杂性中了解有关代码度量的详细信息。

如何解决冲突

若要解决此规则的冲突,请重构方法以降低其圈复杂度。

何时禁止显示警告

如果无法轻松降低复杂度,并且该方法易于理解、测试和维护,则可禁止显示此规则的警告。 具体而言,包含较大 switch(在 Visual Basic 中为 Select)语句的方法就是可排除的候选方法。 在开发周期后期破坏代码库稳定性或在先前发布的代码中引入意外的运行时行为更改的风险可能超过重构代码的可维护性优势。

注意

如果满足以下所有条件,你可能会看到来自此规则的误报警告:

  • 你将 Visual Studio 2022 版本 17.5 或更高版本与旧版 .NET SDK(即 .NET 6 或更低版本)配合使用。
  • 你使用的是 .NET 6 SDK 中的分析器或较旧版本的分析器包,例如 Microsoft.CodeAnalysis.FxCopAnalyzers。

误报是由于 C# 编译器中的中断性变更造成的。 请考虑使用更新的包含误报警告修补程序的分析器。 升级到 Microsoft.CodeAnalysis.NetAnalyzers 版本 7.0.0-preview1.22464.1 或更高版本,或使用 .NET 7 SDK 中的分析器。

抑制警告

如果只想抑制单个冲突,请将预处理器指令添加到源文件以禁用该规则,然后重新启用该规则。

#pragma warning disable CA1502
// The code that's violating the rule is on this line.
#pragma warning restore CA1502

若要对文件、文件夹或项目禁用该规则,请在配置文件中将其严重性设置为 none

[*.{cs,vb}]
dotnet_diagnostic.CA1502.severity = none

有关详细信息,请参阅如何禁止显示代码分析警告

配置阈值

可以配置此规则触发的阈值以及要分析的符号类型。 允许的符号类型包括:

  • Assembly
  • Namespace
  • Type
  • Method
  • Field
  • Event
  • Property
  1. 创建名为 CodeMetricsConfig.txt 的文本文件。

  2. 采用以下格式将所需的阈值添加到文本文件中:

    CA1502: 10
    

    在此示例中,规则配置为在方法的圈复杂度大于 10 时触发。

    CA1502(Type): 4
    

    在此示例中,规则配置为在类型的圈复杂度大于 4 时触发。 使用此配置文件时,规则将继续报告圈复杂度大于默认值 (25) 的方法。

  3. 在项目文件中,将配置文件的生成操作标记为 AdditionalFiles。 例如:

    <ItemGroup>
      <AdditionalFiles Include="CodeMetricsConfig.txt" />
    </ItemGroup>
    

如何计算圈复杂度

圈复杂度的计算方法是,将以下各项加 1:

  • 分支数(例如 ifwhiledo)。
  • switch 中的 case 语句数。

示例

下面的示例演示具有不同圈复杂度的方法。

圈复杂度为 1

public void Method()
{
    Console.WriteLine("Hello World!");
}
Public Sub Method()
    Console.WriteLine("Hello World!")
End Sub

圈复杂度为 2

void Method(bool condition)
{
    if (condition)
    {
        Console.WriteLine("Hello World!");
    }
}
Public Sub Method(ByVal condition As Boolean)
    If (condition) Then
        Console.WriteLine("Hello World!")
    End If
End Sub

圈复杂度为 3

public void Method(bool condition1, bool condition2)
{
    if (condition1 || condition2)
    {
        Console.WriteLine("Hello World!");
    }
}
Public Sub Method(ByVal condition1 As Boolean, ByVal condition2 As Boolean)
    If (condition1 OrElse condition2) Then
        Console.WriteLine("Hello World!")
    End If
End Sub

圈复杂度为 8

public void Method(DayOfWeek day)
{
    switch (day)
    {
        case DayOfWeek.Monday:
            Console.WriteLine("Today is Monday!");
            break;
        case DayOfWeek.Tuesday:
            Console.WriteLine("Today is Tuesday!");
            break;
        case DayOfWeek.Wednesday:
            Console.WriteLine("Today is Wednesday!");
            break;
        case DayOfWeek.Thursday:
            Console.WriteLine("Today is Thursday!");
            break;
        case DayOfWeek.Friday:
            Console.WriteLine("Today is Friday!");
            break;
        case DayOfWeek.Saturday:
            Console.WriteLine("Today is Saturday!");
            break;
        case DayOfWeek.Sunday:
            Console.WriteLine("Today is Sunday!");
            break;
    }
}
Public Sub Method(ByVal day As DayOfWeek)
    Select Case day
        Case DayOfWeek.Monday
            Console.WriteLine("Today is Monday!")
        Case DayOfWeek.Tuesday
            Console.WriteLine("Today is Tuesday!")
        Case DayOfWeek.Wednesday
            Console.WriteLine("Today is Wednesday!")
        Case DayOfWeek.Thursday
            Console.WriteLine("Today is Thursday!")
        Case DayOfWeek.Friday
            Console.WriteLine("Today is Friday!")
        Case DayOfWeek.Saturday
            Console.WriteLine("Today is Saturday!")
        Case DayOfWeek.Sunday
            Console.WriteLine("Today is Sunday!")
    End Select
End Sub

CA1501:避免过度继承

另请参阅