目前,如果我要检查一个解决方案,我选择内循环引用Architecture - Generate Dependency Graph - For Solution
。 然后从打开的新标签我选择Layout - Analyzers - Circular References Analyzer
。 最后,如果我从各个组件深入和有循环引用我可以看到他们以红色突出显示在图形上,他们也出现在错误列表警告。
由于我打算即使是同一类的方法之间发现循环引用,这是很容易出错,耗时一个中等大小的代码库。
我想知道是否有一种方式来获得一次所有的警告,而不必展开节点,或可能打开高亮显示的父节点,这样我只能在一定包含循环引用组装深入。
NDepend的应该能够帮助,但我更喜欢让事情尽可能的简单,所以我总是警惕采取额外的工具。
是NDepend的可查找循环引用有效地让我解释如何,因为它可能比你想象的容易( 声明:我对NDepend的开发商之一 )。 到目前为止,您可以找到命名空间依赖循环外的开箱 ,但正如我在下面解释,很容易在一个命名空间中找到,以及类型之间的循环,或类型的方法 。
有一个默认的C#代码LINQ规则列出的命名空间依赖循环。 然后,这种循环可以导出到依赖图,或依赖矩阵。 这里是关于罗斯林代码库CTP 2012年6月执行的规则的截图(注意只用了16毫秒运行)。 它发现有11个不同周期,并在屏幕截图所示,可以钻到每个周期和周期导出到图中:
这里是7个命名空间周期的依赖关系图。 请注意,它看起来比只是一个典型的O型圈循环更加复杂。 这里的关键是,从这些命名空间,就可以到达所有其他的人。 这是周期(纠缠)的广义概念。
在的代码默认的C#代码LINQ规则列出的命名空间依赖循环乍一看看上去令人生畏。 但是C#开发人员应该了解它在几分钟内,然后可以修改它很容易地找到任何种类的依赖循环的。
例如,为了找到( 相同组件的周期 ,而不是名称空间 ) 相同类型的周期的方法几乎是如方法替换所有名称空间字,和组装字由类型一样简单。
// <Name>Avoid methods of a type to be in cycles</Name>
warnif count > 0
from t in Application.Types
.Where(t => t.ContainsMethodDependencyCycle != null &&
t.ContainsMethodDependencyCycle.Value)
// Optimization: restreint methods set
// A method involved in a cycle necessarily have a null Level.
let methodsSuspect = t.Methods.Where(m => m.Level == null)
// hashset is used to avoid iterating again on methods already caught in a cycle.
let hashset = new HashSet<IMethod>()
from suspect in methodsSuspect
// By commenting this line, the query matches all methods involved in a cycle.
where !hashset.Contains(suspect)
// Define 2 code metrics
// - Methods depth of is using indirectly the suspect method.
// - Methods depth of is used by the suspect method indirectly.
// Note: for direct usage the depth is equal to 1.
let methodsUserDepth = methodsSuspect.DepthOfIsUsing(suspect)
let methodsUsedDepth = methodsSuspect.DepthOfIsUsedBy(suspect)
// Select methods that are both using and used by methodSuspect
let usersAndUsed = from n in methodsSuspect where
methodsUserDepth[n] > 0 &&
methodsUsedDepth[n] > 0
select n
where usersAndUsed.Count() > 0
// Here we've found method(s) both using and used by the suspect method.
// A cycle involving the suspect method is found!
let cycle = usersAndUsed.Append(suspect)
// Fill hashset with methods in the cycle.
// .ToArray() is needed to force the iterating process.
let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray()
select new { suspect, cycle }
...这里是这个规则的结果看起来像(仍与该方法循环导出到依赖图或矩阵的可能性)。 注意,由于方法和类型的数量比命名空间和组件的数量要高得多,这个查询了就像在一个大的代码库像罗斯林(而不是16毫秒的命名空间周期)进行试验10秒钟,这样你可能需要调整所述CQLinq查询执行超时(这是默认每2秒)。
是完整的,我注意到的是,周期是大部分由少数双向引用挑起的时间(即A正在使用B,B是使用)。 因此,消除双向引用是做打破周期的第一件事。 这就是为什么我们提供的默认CQLinq规则避免名称空间相互依存的 ,仍然可适应类型或方法周期。