圈复杂度数为 31,这是从哪里来的?
Posted
技术标签:
【中文标题】圈复杂度数为 31,这是从哪里来的?【英文标题】:Cyclomatic complexity count of 31, where has this come from? 【发布时间】:2012-12-06 10:08:46 【问题描述】:我正在开发一个从 Excel 文件中提取数据的应用程序(我无权访问实际数据库),并且我编写了一个方法,该方法具有从 Excel 电子表格中提取数据的唯一功能,如下所示。
private IEnumerable<SMEntity> ExtractSMData(List<MSExcel.Range> SMData)
List<SMEntity> SMEntities = new List<SMEntity>();
foreach (MSExcel.Range Row in SMData)
SMEntity entity = new SMEntity();
entity.IncidentNumber = Row.get_Range("K1").get_Value();
entity.SRNumber = Row.get_Range("L1").get_Value();
entity.SRCategory = Row.get_Range("M1").get_Value();
entity.SiebelClientCall = EntityConversions.DateTimeConversion(Row.get_Range("N1").get_Value());
entity.SiebelOpenedDate = EntityConversions.DateTimeConversion(Row.get_Range("O1").get_Value());
entity.IncidentOpenDate = EntityConversions.DateTimeConversion(Row.get_Range("P1").get_Value());
entity.PickedUpBeforeClient = Row.get_Range("Q1").get_Value().ToString().ToLowerCase() == "no" ? false : true;
entity.OutageStartTime = EntityConversions.DateTimeConversion(Row.get_Range("R1").get_Value());
entity.DetectionPoint = EntityConversions.DateTimeConversion(Row.get_Range("S1").get_Value());
entity.SecondsToDetection = EntityConversions.ConvertDetectionTimeToInt(Row.get_Range("T1").get_Value());
entity.OutageEndTime = EntityConversions.DateTimeConversion(Row.get_Range("U1").get_Value());
entity.MTTR = EntityConversions.ConvertMTTRStringToInt(Row.get_Range("V1").get_Value());
entity.RepairedOnTime = Row.get_Range("W1").get_Value().ToString().ToLowerCase() == "no" ? false : true;
SMEntities.Add(entity);
return SMEntities;
我已经运行了代码分析(我正在使用 Visual Studio 2012 并在 .NET 4.5 中进行开发)并且我有一个 CA1502: Avoid excessive complexity
(复制如下)。作为一名初级开发人员(我今年 17 岁),我尝试使用 MSDN 了解更多有关此内容的信息,但是,我对为什么我的圈复杂度为 33 感到有些困惑。
CA1502
避免过于复杂
'Extraction.ExtractSMData(List<Range>)'
有一个圈 复杂度为 33. 重写或重构方法以降低复杂度 到 25 岁。
Core.Extraction.cs:104
我可以通过我的快速假设(condition ? if_true : if_false
,这些叫什么?)看到它可能很糟糕,但我仍然只能将其视为 5。
更新:
圈复杂度现在为 33...
如果我注释掉 entity.IncidentNumber = Row.get_Range("K1").get_Value();
,复杂度变为 32。我认为 get_Range()
和 get_Value()
各为一个,但还好...
如果我注释掉 entity.RepairedOnTime = Row.get_Range("W1").get_Value().ToString().ToLower() == "no" ? false : true;
,复杂度将变为 28...
get_Range()
, get_Value()
, quick-if 是 3,ToString()
和 ToLower()
算吗?
【问题讨论】:
您的“quick-ifs”被称为conditional operator。不过,我喜欢你使用“快速如果”这个短语。它很整洁。 ;) @alexh 它不被称为 the 三元运算符 - 它是 条件运算符 它是 a 三元运算符bool x = Row.get_Range("W1").get_Value().ToString().ToLowerCase() == "no" ? false : true;
可以替换为bool x = Row.get_Range("W1").get_Value().ToString().ToLowerCase() != "no"
,这不是条件语句,因此您可以稍微降低复杂性。
@Andras Zoltan,感谢您的精确:我们在我国使用“三元”术语来定义此运算符。这是一个错误
此外,谷歌搜索的内容是“?:运算符”,因为冒号是运算符的一部分。 =)
【参考方案1】:
我计算了方法本身的复杂度,foreach
和两个条件运算符总共 4 个。如果对 get_Range
的 13 次调用中的每一个都值得 +1 复杂度,并且对 get_Value
的 13 次调用中的每一个都值得 +1 复杂度,那么总复杂度将加起来为 30(仍然是 1 短,但接近)。我不确定为什么这两个函数会增加复杂性,但这似乎是合理的。
尝试删除调用 get_Range
和 get_Value
的行之一,看看圈复杂度是否降至 29。
【讨论】:
FxCop 作用于生成的 IL 代码。我假设编译器会为每个调用插入一些 COM 互操作代码(例如检查空值)。有 28 个 COM 调用(13xget_Range,13xget_value,GetEnumerator,GetNext),2x ?: + foreach = 31 检查更新家伙。我也这么认为,阿德里安,但现在已经 33 岁了!【参考方案2】:您的返回类型是 IEnumerable,所以不要使用列表。这使得 IENumerable 毫无用处。 否则你不使用惰性评估 见:http://blogs.msdn.com/b/pedram/archive/2007/06/02/lazy-evaluation-in-c.aspx
那么最好使用yield return:
private IEnumerable<SMEntity> ExtractSMData(List<MSExcel.Range> SMData)
foreach (MSExcel.Range Row in SMData)
SMEntity entity = new SMEntity();
entity.IncidentNumber = Row.get_Range("K1").get_Value();
entity.SRNumber = Row.get_Range("L1").get_Value();
entity.SRCategory = Row.get_Range("M1").get_Value();
entity.PickedUpBeforeClient = !Row.get_Range("Q1").get_Value().ToString().ToLowerCase() == "no"
entity.RepairedOnTime = !Row.get_Range("W1").get_Value().ToString().ToLowerCase() == "no"
entity.SiebelClientCall = EntityConversions.DateTimeConversion(Row.get_Range("N1").get_Value());
entity.SiebelOpenedDate = EntityConversions.DateTimeConversion(Row.get_Range("O1").get_Value());
entity.IncidentOpenDate = EntityConversions.DateTimeConversion(Row.get_Range("P1").get_Value());
entity.OutageStartTime = EntityConversions.DateTimeConversion(Row.get_Range("R1").get_Value());
entity.DetectionPoint = EntityConversions.DateTimeConversion(Row.get_Range("S1").get_Value());
entity.OutageEndTime = EntityConversions.DateTimeConversion(Row.get_Range("U1").get_Value());
entity.MTTR = EntityConversions.ConvertMTTRStringToInt(Row.get_Range("V1").get_Value());
entity.SecondsToDetection = EntityConversions.ConvertDetectionTimeToInt(Row.get_Range("T1").get_Value());
yield return entity;
你也可以这样写:
private IEnumerable<SMEntity> ExtractSMData(List<MSExcel.Range> SMData)
foreach (MSExcel.Range Row in SMData)
yield return new SMEntity
IncidentNumber = Row.get_Range("K1").get_Value(),
SRNumber = Row.get_Range("L1").get_Value(),
SRCategory = Row.get_Range("M1").get_Value(),
PickedUpBeforeClient = !Row.get_Range("Q1").get_Value().ToString().ToLowerCase() == "no"
RepairedOnTime = !Row.get_Range("W1").get_Value().ToString().ToLowerCase() == "no"
SiebelClientCall = EntityConversions.DateTimeConversion(Row.get_Range("N1").get_Value()),
SiebelOpenedDate = EntityConversions.DateTimeConversion(Row.get_Range("O1").get_Value()),
IncidentOpenDate = EntityConversions.DateTimeConversion(Row.get_Range("P1").get_Value()),
OutageStartTime = EntityConversions.DateTimeConversion(Row.get_Range("R1").get_Value()),
DetectionPoint = EntityConversions.DateTimeConversion(Row.get_Range("S1").get_Value()),
OutageEndTime = EntityConversions.DateTimeConversion(Row.get_Range("U1").get_Value()),
MTTR = EntityConversions.ConvertMTTRStringToInt(Row.get_Range("V1").get_Value()),
SecondsToDetection = EntityConversions.ConvertDetectionTimeToInt(Row.get_Range("T1").get_Value())
;
【讨论】:
惰性求值非常适合简单的内存中的东西。在处理外部依赖项或其他复杂的事情时,您最好渴望能够处理错误。 IEnumerable 的使用者不应该根据方法的实现细节来处理 COMException、SqlExcepton、IOException 等? “源”-数据已经在内存中,因为函数需要一个列表作为参数。我认为这归结为“个人偏好”。如果他想一次失败所有行,或者在可能的错误发生之前检索一些。以上是关于圈复杂度数为 31,这是从哪里来的?的主要内容,如果未能解决你的问题,请参考以下文章