Visual Studio 2012 — эффективный поиск циклических ссылок

В настоящее время, если я хочу проверить циклические ссылки внутри решения, я выбираю Architecture - Generate Dependency Graph - For Solution. Затем в открывшейся новой вкладке я выбираю Layout - Analyzers - Circular References Analyzer. Наконец, если я перейду к отдельным сборкам и найду круговые ссылки, я увижу, что они выделены красным цветом на графике, и они также отображаются в виде предупреждений в списке ошибок.

Поскольку я намереваюсь обнаруживать циклические ссылки даже между методами одного и того же класса, это довольно подвержено ошибкам и занимает много времени в умеренно большой кодовой базе.

Я хотел бы знать, есть ли способ получить все предупреждения сразу, не расширяя узлы, или, может быть, включить подсветку для родительских узлов, чтобы я мог детализировать только те сборки, которые наверняка содержат циклические ссылки.

NDepend должен быть в состоянии помочь, но я предпочитаю, чтобы все было как можно проще, поэтому я всегда с осторожностью отношусь к использованию дополнительных инструментов.


person RobSullivan    schedule 27.02.2013    source источник
comment
Связанная утилита для быстрого поиска циклических ссылок: stackoverflow.com/a/43374622/64334   -  person Ronnie Overby    schedule 12.04.2017


Ответы (1)


Да, 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
comment
Я установил Ndepend, я скопировал и вставил ваше правило и потерпел неудачу, потому что он выделяет usersAndUsed и дает ошибку, вызов неоднозначен между следующими методами или свойствами... - person Zyo; 06.02.2021
comment
Я изменил запрос, чтобы он снова работал. Проблема заключалась в том, что в .NET BCL был добавлен метод расширения Append(), который теперь конфликтует с методом расширения NDepend Append(). - person Patrick from NDepend team; 08.02.2021