Да, NDepend может эффективно находить циклические ссылки, позвольте мне объяснить, как это может быть проще, чем вы думаете (Отказ от ответственности: я являюсь одним из разработчиков NDepend). До сих пор вы могли найти цикл зависимостей namespace готовый, но, как я объясню ниже, также легко найти циклы между типы в пространстве имен или методы типа.
Существует правило кода C# LINQ по умолчанию, в котором перечислены циклы зависимости пространств имен. Затем такой цикл можно экспортировать в граф зависимостей или матрицу зависимостей. Вот скриншот правила, выполненного в CTP-кодовой базе Roslyn в июне 2012 года (обратите внимание, что выполнение заняло всего 16 миллисекунд). Было найдено 11 различных циклов, и, как показано на снимке экрана, вы можете детализировать каждый цикл и экспортировать цикл на график:

Вот график зависимостей цикла 7 пространств имен. Обратите внимание, что это выглядит сложнее, чем просто классический цикл с уплотнительным кольцом. Ключевым моментом здесь является то, что из любого из этих пространств имен вы можете получить доступ ко всем остальным. Это обобщенное понятие цикла (запутывания).

Код кодового правила 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!
// 8Feb2021: invoke extension method Append() explicitly to avoid ambiguous compiler error
// because of the new .NET BCL extension methods Append() method
let cycle = NDepend.Helpers.ExtensionMethodsEnumerable.Append(usersAndUsed,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 }
...и вот как выглядит результат этого правила (еще с возможностью экспорта цикла метода в граф зависимостей или матрицу). Обратите внимание, что поскольку количество методов и типов намного превышает количество пространств имен и сборок, выполнение этого запроса в большой кодовой базе, такой как Roslyn, заняло около 10 секунд (вместо 16 мс для цикла пространств имен), поэтому вам может потребоваться настроить время ожидания выполнения запроса CQLinq (которое по умолчанию составляет 2 секунды).

Чтобы быть полным, я заметил, что цикл большую часть времени провоцируется несколькими двунаправленными ссылками (т.е. A использует B, B использует A). Следовательно, удаление двунаправленных ссылок — это первое, что нужно сделать, чтобы разорвать цикл. Именно поэтому мы предоставили правило CQLinq по умолчанию Избегайте взаимной зависимости пространств имен, которые все еще могут быть адаптированы к циклам типов или методов.
person
Patrick from NDepend team
schedule
28.02.2013