【问题标题】:Why Does This Maintainability Index Increase?为什么这个可维护性指数会增加?
【发布时间】:2011-02-14 11:58:46
【问题描述】:

如果有人可以根据 Visual Studio 的代码度量规则向我解释以下两段代码之间的区别,我将不胜感激。如果我不将所有内容都封装在 using ( ) 中,为什么可维护性指数会略有增加?

样本 1MI 得分为 71

public static String Sha1(String plainText)
{
    using (SHA1Managed sha1 = new SHA1Managed())
    {
        Byte[] text = Encoding.Unicode.GetBytes(plainText);
        Byte[] hashBytes = sha1.ComputeHash(text);
        return Convert.ToBase64String(hashBytes);    
    }
}

样本 2MI 得分为 73

public static String Sha1(String plainText)
{
    Byte[] text, hashBytes;
    using (SHA1Managed sha1 = new SHA1Managed())
    {
        text = Encoding.Unicode.GetBytes(plainText);
        hashBytes = sha1.ComputeHash(text);
    }
    return Convert.ToBase64String(hashBytes);   
}

我理解指标在更广泛的背景和理解之外毫无意义,程序员应该谨慎行事。虽然我可以使用return Convert.ToBase64String(sha1.ComputeHash(Encoding.Unicode.GetBytes(plainText))) 将分数提高到 76,但我不应该这样做。我显然只是在玩数字,那时它并没有真正更具可读性或可维护性。我很好奇这种情况下增加背后的逻辑可能是什么。这显然不是行数。

【问题讨论】:

  • 围绕这个问题的讨论及其各种答案表明,可维护性指数不是很直观——另请参阅我的post on the maintainability index,讨论该指标的各种问题。

标签: c# refactoring metrics code-metrics


【解决方案1】:

将变量全部放在顶部,这样您就知道函数中的内容更“可维护”,至少决定代码度量规则的人是这么认为的。

这是真的吗?完全取决于编写代码的团队。似乎您已经从问题的语气中知道了这一点,但是对几乎所有代码指标都持保留态度,它们是某人认为最好的,这对于外部团队可能并非如此microsoft...做对您的团队最有利的事情,而不是某些计算器告诉您的事情。

我不会做出对您和您团队的编码性能有害的更改(除非它是为了实际性能或改进错误处理等),您认为在指标板上获得一些分数时可读性较差。

话虽如此,如果它给你一个非常低的可维护性,那么可能有一些值得关注或分解成更小块的东西,因为非常低的分数可能不是那么可接受,因为几乎任何团队。

【讨论】:

  • 我相信这是正确的——就解释数字而言——但我认为可维护性计算在这方面已经过时了。曾几何时,在顶部声明所有变量是有意义的 - 曾几何时(某些)语言需要它! ——但那个时代早已过去。今天,最小化标识符的生命周期对可维护性的贡献更大。
  • 这是不正确的:指标不关心变量的位置(尽管我认为现在已经很好地接受了接近使用是一个优势)。正如 Dan Bryant 指出的那样,您只是看到在一行中声明两个变量的效果(意味着 Byte[] 在第二种方法中只出现一次),使得该方法在 Halstead Volume 方面“更短”。
【解决方案2】:

因为变量的声明和它们的使用位置之间的距离增加了。

规则是尽量减小变量span,span是变量声明到使用位置的距离。随着这个距离的增加,在程序员进一步意识到代码中的影响的情况下,引入稍后影响变量的代码的风险也会增加。

这里是一本好书的链接,该书涵盖了这个和许多其他关于代码质量的主题。 http://www.amazon.com/Code-Complete-Practical-Handbook-Construction/dp/0735619670/ref=dp_ob_title_bk

【讨论】:

  • 这似乎与问题相反,因为声明和使用之间的距离在较高分数中更大。
  • @Damian - 解释与结果相反,我同意这是明智的,但没有解释问题。我也更喜欢分数较低的,但根据这个答案,它应该有一个更高的分数,它没有。
  • @Nick 我想知道 OP 是否以错误的方式获取了这些值。我在 VS2010 中对其进行了测试,但似乎没有。虽然我得到了不同的值:70 和 69。很奇怪,但突出了你的观点,即它应该加一点盐。
  • 我一直在考虑这个问题,虽然我想只有与分析引擎密切相关的人才能对此给出真正的解释,但我想知道分析引擎是否正在测量行长并说通过调用函数来声明和初始化变量的“复杂性”正在影响指标。我现在无法访问 VS,可能有趣的尝试是获取第一个代码并将变量的声明移动到 using 子句的范围内,但仍然在单独的行上进行初始化,这会带来什么?
  • 相同的数字,@Chris。变量在 using() 块内定义和使用时的值低于在块外定义时的值。
【解决方案3】:

我自己,我宁愿看到return Convert.ToBase64String(sha1.ComputeHash(Encoding.Unicode.GetBytes(plainText)));这是一个应该而不是一个不应该。这种形式的优点是简洁地表达了实际的数据流;如果你添加了一堆临时变量和赋值,我现在必须读取变量名并匹配它们的出现,看看实际发生了什么。

【讨论】:

  • 我不同意。在一行中,您必须首先目视扫描以找到最内层的表达式 (plainText),然后向后搜索到 GetBytes。 “好的,我们得到了文本的字节”。然后sha1.ComputeHash(“好的,现在我们采用 SHA1”),最后以 64 为基数。如果你把它分成几行,我认为逐行“逐步执行”要容易得多,并理解发生了什么。如果选择智能变量名称,则不必“匹配它们的出现”,这很有意义。
  • 实际上单行表示的数据流正好相反,因为方法调用基本上是前缀表示法。实际流程是plainText -> GetBytes -> ComputeHash -> ToBase64String。逐行排列显示了这一点。您必须从右到左阅读的单行字。而且调试起来要困难得多,因为您看不到中间步骤。假装有一个空引用异常。逐行将立即显示给您。单线怎么样?
  • @MarkSowul 我同意 100%。我总是更喜欢可读性,而不是一个具有多个功能链的班轮。如果我的团队成员之一这样做,我会告诉他/她重构它。
【解决方案4】:

这是一个老问题,但我只是想补充一点,MI 部分基于Halstead volume,它基于“运算符”和“操作数”的计数。如果按类型声明变量是“运算符”,这意味着样本 2 的运算符较少,从而改变了分数。一般来说,由于 MI 是一种统计测量,因此在处理小样本量时(如单个短方法)的用处有限。

【讨论】:

  • 有趣的一点,不知道把Byte[] text,hashBytes分成两行会不会降低分数
  • @MarkSowul:这不会影响 Halstead,但会影响代码行,这也是可维护性索引的组成部分。
猜你喜欢
  • 1970-01-01
  • 2016-06-03
  • 2017-04-13
  • 1970-01-01
  • 1970-01-01
  • 2019-09-28
  • 1970-01-01
  • 1970-01-01
  • 2018-05-24
相关资源
最近更新 更多