Да NDepend могут Найти циклические ссылки позволяют эффективно объяснить, как потому, что это может быть проще, чем вы могли бы подумать (Отказ от ответственности: Я один из разработчиков на NDepend). До сих пор можно найти имена зависимости цикла вне коробки, но, как я объясняю ниже, легко найти, а циклы между типами в пространстве имен или методами из тип.
Существует default C# LINQ code rule, в котором перечислены циклы зависимостей пространств имен. Затем этот цикл можно экспортировать в граф зависимости или матрицу зависимостей. Вот скриншот правила, выполненного на базе кода Roslyn CTP, июнь 2012 года (уведомление было потрачено всего 16 миллисекунд). Он нашел 11 различных циклов, и, как показано на скриншоте, вы можете углубиться в каждом цикле и экспортировать цикл на графике:
Здесь представлен график зависимости цикла 7 пространств имен. Обратите внимание, что это выглядит более сложным, чем просто классический цикл O-ring. Ключевым моментом здесь является то, что из любого из этих пространств имен вы можете охватить все остальные. Это обобщенное понятие цикла (перепутывание).
код в default C# LINQ code rule, в котором перечислены циклы пространства имен зависимостей может выглядеть сложной на первый взгляд. Но разработчик 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 }
... и вот как результат этого правила выглядит (по-прежнему с возможностью экспорта цикла метод в графе зависимостей или матрицы).Обратите внимание: поскольку число методов и типов намного превышает количество пространств имен и сборок, этот запрос занял 10 секунд для работы на большой базе кода, такой как Roslyn (вместо 16 мс для цикла пространств имен), поэтому вам может потребоваться настроить тайм-аут выполнения запроса CQLinq (по умолчанию - 2 секунды).
Чтобы быть полным, что я заметил, что цикл является большой частью времени, спровоцированного несколько двунаправленных ссылок (т.е. А с помощью B, B использует A). Следовательно, удаление двунаправленных ссылок - первое, что нужно сделать, чтобы разбить цикл. Вот почему мы предоставили правило CQLinq по умолчанию Avoid namespaces mutually dependent, которое все еще может быть адаптировано к типам или методам.
Связанные утилиты для быстрого поиска круглых ссылок: http://stackoverflow.com/a/43374622/64334 –