的Visual Studio 2012 - 寻找循环引用高效高效、Visual、Studio

2023-09-03 12:55:19 作者:爱情算个屁

目前,如果我想检查我选择的解决方案内循环引用建筑 - 生成依赖图 - 对于解决方案。 然后从打开的新标签我选择布局 - 分析仪 - 循环引用分析仪。 最后,如​​果我钻到从各个组件和有循环引用我可以看到他们以红色突出显示在图形上,他们也出现在错误列表警告。

Currently if I want to check for circular references inside a solution I select Architecture - Generate Dependency Graph - For Solution. Then from the new tab that opens I select Layout - Analyzers - Circular References Analyzer. Finally if I drill down from the individual assemblies and there are circular references I can see them highlighted in red on the graph and they also appear as warnings in the Error List.

由于我打算发现即使是同一个类的方法之间的循环引用,这是很容易出错,在一个中等大的codeBase的耗时。

Since I intend to spot circular references even between methods of the same class this is quite error prone and time consuming on a moderately large codebase.

我想知道是否有一种方式来获得一次所有的警告,而无需展开节点或者打开高亮显示的父节点,这样我只能在组装了肯定包含循环引用钻到

I'd like to know if there is a way to get all the warnings at once without having to expand the nodes or maybe to turn on highlighting for parent nodes so that I can drill down only on assembles that surely contain circular references.

NDepend的应该可以帮助,但我preFER让事情尽可能的简单,所以我一直持谨慎态度采取额外的工具。

NDepend should be able to help but I prefer to keep things as simple as possible so I'm always wary about adopting additional tools.

推荐答案

是NDepend的可的查找循环引用有效地的让我解释,因为它可能比你想象的(更简单的免责声明:我的开发商对NDepend的一个的)。到目前为止,你可以找到的命名空间的依赖循环的外的开箱即用的,但是,因为我解释下面,很容易找到的,以及周期的在命名空间中,或方法的类型的类型的。

Yes NDepend can Find Circular References Efficiently let me explain how because it might be easier than you might think (Disclaimer: I am one of the developers on NDepend). So far you can find namespace dependency cycle out-of-the-box, but, as I am explaining below, it is easy to find as well cycles between types in a namespace, or methods of a type.

有一个默认的C#LINQ code规则的列出命名空间依赖循环。这样的循环就可以导出到依赖图,或依赖矩阵。这里是关于罗斯林code基2012 CTP六月执行规则的截图(注意只用了16毫秒运行)。结果发现11不同的周期,如图所示的截图,你可以深入到每个周期,一个周期导出到图:

There is a default C# LINQ code rule that lists namespaces dependency cycles. Such cycle can then be exported to the dependency graph, or dependency matrix. Here is a screenshot of the rule executed on the Roslyn code base CTP June 2012 (notice it took only 16 milliseconds to run). It found 11 distinct cycles, and as shown on the screenshot, you can drill down into each cycle and export a cycle to the graph:

下面是7的命名空间周期的依赖关系图。请注意,它看起来比只是一个典型的O型圈循环更加复杂。这里的关键是,从这些命名空间中,可以达到所有其他的人。这是一个循环的广义概念(纠缠)。

Here is a dependency graph of the 7 namespaces cycle. Notice that it looks more complicated than just a classic O-ring cycle. The key here, is that from any of these namespaces, you can reach all the other ones. This is the generalized notion of cycle (entangling).

在默认的C#LINQ code规则列出命名空间依赖循环可能看起来令人生畏乍一看。但是C#开发人员应该了解它在几分钟内,然后可以很容易地适应它来寻找任何一种依赖周期。

The code of the default C# LINQ code rule that lists namespaces dependency cycles might look daunting at first glance. But a C# developer should understand it in a few minutes and can then adapt it easily to find any kind of dependency cycle.

例如,要查找的相同类型的循环(而不是相同的装配周期的命名空间),这几乎是简单,只要更换所有的命名空间字通过的方法和组装的字通过的键入的。

For example, to find methods of same types cycles (instead of namespaces of same assembly cycles) it is almost as simple as replacing all namespace word by method, and assembly word by type.

// <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 }

...这里是如何将此规则的结果如下(仍与该方法循环导出到依赖图或矩阵的可能性)。请注意,由于方法和类型的数量小于命名空间和组件的数量要高得多,这个查询了像10秒到像罗斯林大code基(而不是16毫秒为名称空间周期)来运行,所以你可能需要调整CQLinq查询执行超时(这是2秒按默认值)。

...and here is how the result of this rule looks like (still with the possibility to export the method cycle to the dependency graph or matrix). Notice that since the number of methods and types is much higher than the number of namespaces and assemblies, this query took like 10 seconds to be run on a large code base like Roslyn (instead of 16ms for namespaces cycle) so you might need to adjust the CQLinq query execution time-out (which is 2 seconds per default).

要完成,我注意到的是,周期是大部分由少数双向引用挑起的时间(即A是使用B,B是一个使用)。因此,除去双向引用是做打破循环的第一件事。这就是为什么我们提供的默认CQLinq规则避免命名空间中互相依赖的,即仍然可以适于类型或方法周期。

To be complete, what I noticed is that cycle are most of the time provoked by a few bi-directional references (i.e A is using B, B is using A). Hence removing bi-directional references is the first thing to do to break cycle. This is why we provided the default CQLinq rule Avoid namespaces mutually dependent, that can still be adapted to types or methods cycles.