8

我们有这个代码,排序:

private void InitializeEvents()
{
    this.Event1 += (s,e) => { };
    this.Event2 += (s,e) => { };
    this.Event3 += (s,e) => { };
    this.Event4 += (s,e) => { };
    this.Event5 += (s,e) => { };
    this.Event6 += (s,e) => { };
    this.Event7 += (s,e) => { };
    this.Event8 += (s,e) => { };
    this.Event9 += (s,e) => { };
    this.Event10 += (s,e) => { };
    this.Event11 += (s,e) => { };
    this.Event12 += (s,e) => { };
    this.Event13 += (s,e) => { };
}

VS10 Ultimate 中的代码分析说“27 的圈复杂度”。删除其中一条线使圈复杂度为 25。

没有分支正在进行,所以这怎么可能?

4

3 回答 3

17

请记住,代码分析正在查看您的程序集中的 IL,而不是您的源代码。IL 中没有任何东西本身支持 lambda 表达式,因此它们是编译器的构造。您可以在此处找到输出的详细信息。但基本上你的 lambda 表达式变成了一个私有静态类,它是一个匿名委托。但是,与其在代码中每次引用匿名委托时都创建一个实例,不如将委托缓存起来。因此,每次您分配 lambda 表达式时,它都会检查是否已创建该 lambda 委托的实例,如果是,则它使用缓存的委托。这会在 IL 中生成 if/else,将复杂度增加 2。因此,在此函数中,复杂度为 1 + 2*(lambda express) = 1 + 2 *(13) = 27,这是正确的数字。

于 2012-04-20T11:59:49.690 回答
3

C# 编译器实际上为匿名方法生成了一些相当“有趣”的 IL,包括 lambda。对于每一个,它都会创建一个私有字段,然后在消费方法中分配其值之前,它会检查该值是否为空,这会在编译的方法中添加一个 If 分支。代码度量工具应该忽略这一点(http://social.msdn.microsoft.com/Forums/eu/vstscode/thread/8c17f569-5ee3-4d26-bf09-4ad4f9289705,https://connect.microsoft.com/VisualStudio /feedback/details/555560/method-using-many-lambda-expressions-causes-high-cyclomatic-complexity),我们可以希望它最终会。目前,如果您认为这是误报,您几乎必须忽略该问题。

于 2012-04-20T12:05:28.250 回答
1

最好的猜测是,这可能是由于上面的语句被转换为事件访问器格式,即

class MyClass
{
  private event EventHandler MyPrivateEvent;

  public event EventHandler MyEvent
  {
    add
    {
      MyPrivateEvent += value;
    }
    remove
    {
      MyPrivateEvent -= value;
    }
  }
}

有关事件访问器格式的讨论,请参阅http://msdn.microsoft.com/en-us/magazine/cc163533.aspxhttp://www.switchonthecode.com/tutorials/csharp-tutorial-event-accessors 。

于 2012-04-20T10:11:11.420 回答