Как программно определить количество ссылок на метод с C#

Я недавно унаследовал консольное приложение C#, которое нуждается в некотором сокращении и очистке. Короче говоря, приложение состоит из одного класса, содержащего более 110000 строк кода. Да, более 110000 строк в одном классе. И, конечно же, приложение является основой нашего бизнеса, круглосуточно обновляя данные, используемые на динамическом веб-сайте. Хотя мне сказали, что мой предшественник был "действительно хорошим программистом", очевидно, что он вообще не был в ООП (или в управлении версиями).

Во всяком случае... во время ознакомления с кодом я нашел множество методов, которые объявлены, но на них никогда не ссылаются. Похоже, что для копирования кода использовалась копия / вставка, например, скажем, у меня есть метод с именем getSomethingImportant(), скорее всего, есть еще один метод с именем getSomethingImortant_July2007() (в большинстве случаев это шаблон functionName_[datetamp]). Похоже, что когда программиста попросили внести изменение в getSomethingImportant(), он скопирует / вставит, затем переименует в getSomethingImortant_Date, внесет изменения в getSomethingImortant_Date, затем изменит любые вызовы методов в коде на имя нового метода, оставив старый метод в код, но никогда не ссылаться.

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

Существуют ли в среде.NET классы, которые я могу использовать для изучения этого кода? Или любые другие полезные инструменты, которые могут помочь идентифицировать методы, которые объявлены, но на которые никогда не ссылаются?

(Дополнительный вопрос: Кто-нибудь еще видел такое приложение на C#, один большой класс? Это более или менее один огромный процедурный процесс, я знаю, что это первый раз, который я видел, по крайней мере, такого размера.)

10 ответов

Решение

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

Чтобы завершить ответ Romain Verdier, давайте немного покопаемся в том, что NDepend может принести вам здесь. (Отказ от ответственности: я разработчик команды NDepend)

NDepend позволяет запрашивать ваш код.NET с помощью некоторых запросов LINQ. Узнать, какие методы вызывают и какие другие вызывают, так же просто, как написать следующий запрос LINQ:

from m in Application.Methods
select new { m, m.MethodsCalled, m.MethodsCallingMe }

Результат этого запроса представлен таким образом, чтобы упростить просмотр вызывающих и вызываемых абонентов (и его 100% интеграция в Visual Studio).

N Зависит от методов звонящих и вызываемых


Есть много других возможностей NDepend, которые могут вам помочь. Например, вы можете щелкнуть правой кнопкой мыши метод в Visual Studio > NDepend > Выбрать методы... > который использует меня (прямо или косвенно)...

N Зависимый метод Visual Studio правой кнопкой мыши

Следующий запрос кода генерируется...

from m in Methods 
let depth0 = m.DepthOfIsUsing("NUnit.Framework.Constraints.ConstraintExpression.Property(String)")
where depth0  >= 0 orderby depth0
select new { m, depth0 }

... который соответствует прямым и косвенным абонентам с глубиной вызовов (1 означает прямой вызывающий абонент, 2 означает вызывающего абонента прямых абонентов и т. д.).

N Зависимый косвенный метод вызывающих

И затем, нажав кнопку " Экспорт в график", вы получите график вызовов вашего сводного метода (конечно, это может быть другой путь, т. Е. Метод, вызываемый прямо или косвенно конкретным сводным методом).

N Зависимый график вызовов

Загрузите бесплатную пробную версию Resharper. Используйте Resharper->Search->Find Usages in File (Ctrl-Shift-F7), чтобы выделить все использования. Также в строке состояния появится счетчик. Если вы хотите искать по нескольким файлам, вы можете сделать это тоже с помощью Ctrl-Alt-F7.

Если вам это не нравится, выполните текстовый поиск по имени функции в Visual Studio (Ctrl-Shift-F), это должно сказать вам, сколько случаев было найдено в решении и где они находятся.

Я не думаю, что вы хотите написать это самостоятельно - просто купите NDepend и используйте его язык запросов кода

FXCop имеет правило, которое идентифицирует неиспользуемые частные методы. Таким образом, вы можете пометить все методы как закрытые и создать список.

У FXCop также есть язык, если вы хотите стать более любопытным http://www.binarycoder.net/fxcop/

В самом.NET Framework нет простого инструмента для этого. Однако я не думаю, что вам действительно нужен список неиспользованных методов сразу. Насколько я понимаю, вы просто просмотрите код и для каждого метода проверите, не используется ли он, а затем удалите его, если так. Я бы использовал Visual Studio "Найти ссылки", чтобы сделать это. В качестве альтернативы вы можете использовать Resharper с его окном "Анализ". Или вы можете просто использовать инструмент анализа кода Visual Studio, чтобы найти все неиспользуемые частные методы.

Окно Analyzer в Reflector может показать вам, где вызывается метод (Used By).
Похоже, что получение информации таким образом займет очень много времени.
Вы могли бы взглянуть на API, который Reflector предоставляет для написания надстроек, и посмотреть, сможете ли вы таким образом выполнить основную работу по анализу. Я ожидаю, что исходный код для надстройки метрик кода может рассказать вам немного о том, как получить информацию о методах из отражателя API.

Изменить: Также может помочь надстройка просмотра модели кода для Reflector. Это хороший способ изучить Reflector API.

Если вы не хотите использовать NDepend, поскольку, похоже, в одной сборке есть только один класс - закомментируйте методы и скомпилируйте. Если он компилируется, удалите их - у вас не будет проблем с наследованием, виртуальных методов или чего-то в этом роде. Я знаю, это звучит примитивно, но иногда рефакторинг - это просто грубая работа, подобная этой. Это отчасти предполагает, что у вас есть модульные тесты, которые вы запускаете после каждой сборки, пока не очистите код (красный / зеленый / рефакторинг).

Я не знаю ничего, что было бы разработано для этого конкретного случая, но вы могли бы использовать Mono.Cecil. Отразите сборки, а затем посчитайте ссылки в IL. Не должно быть слишком жестким.

Попробуйте, чтобы компилятор испускал ассемблерные файлы, как в инструкциях x86, а не.NET-сборок.

Зачем? Потому что разбирать ассемблерный код гораздо проще, чем код C# или сборки.NET.

Например, объявление функции / метода выглядит примерно так:

    .string "w+"
    .text
    .type   create_secure_tmpfile, @function
create_secure_tmpfile:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $-1, -8(%ebp)
    subl    $4, %esp

и ссылки на функции / методы будут выглядеть примерно так:

    subl    $12, %esp
    pushl   24(%ebp)
    call    create_secure_tmpfile
    addl    $16, %esp
    movl    20(%ebp), %edx
    movl    %eax, (%edx)

Когда вы видите "create_secure_tmpfile:", вы знаете, что у вас есть объявление функции / метода, а когда вы видите "call create_secure_tmpfile", вы знаете, что у вас есть ссылка на функцию / метод. Это может быть достаточно для ваших целей, но если нет, это всего лишь несколько шагов, прежде чем вы сможете создать очень симпатичное дерево вызовов для всего приложения.

Другие вопросы по тегам