简体   繁体   English

如何以编程方式识别使用C#的方法引用的数量

[英]How do you programmatically identify the number of references to a method with C#

I've recently inherited C# console application that is in need of some pruning and clean up. 我最近继承了需要修剪和清理的C#控制台应用程序。 Long story short, the app consists of a single class containing over 110,000 lines of code. 简而言之,该应用程序由一个包含超过110,000行代码的类组成。 Yup, over 110,000 lines in a single class. 是的,一个班级超过110,000行。 And, of course, the app is core to our business, running 'round the clock updating data used on a dynamic website. 而且,当然,该应用程序是我们业务的核心,运行'全天候更新动态网站上使用的数据。 Although I'm told my predecessor was "a really good programmer", it obvious he was not at all into OOP (or version control). 虽然我被告知我的前任是“一个非常优秀的程序员”,但显然他根本没有进入OOP(或版本控制)。

Anyway... while familiarizing myself with the code I've found plenty of methods that are declared, but never referenced. 无论如何......在熟悉代码的同时,我发现了许多声明的方法,但从未引用过。 It looks as if copy/paste was used to version the code, for example say I have a method called getSomethingImportant(), chances are there is another method called getSomethingImortant_July2007() (the pattern is functionName_[datestamp] in most cases). 看起来好像使用了复制/粘贴来对代码进行版本化,例如说我有一个名为getSomethingImportant()的方法,很可能还有另一个名为getSomethingImortant_July2007()的方法(在大多数情况下,模式是functionName_ [datestamp])。 It looks like when the programmer was asked to make a change to getSomethingImportant() he would copy/paste then rename to getSomethingImortant_Date, make changes to getSomethingImortant_Date, then change any method calls in the code to the new method name, leaving the old method in the code but never referenced. 看起来当程序员被要求对getSomethingImportant()进行更改时,他会复制/粘贴然后重命名为getSomethingImortant_Date,对getSomethingImortant_Date进行更改,然后将代码中的任何方法调用更改为新方法名称,将旧方法保留为代码但从未引用过。

I'd like to write a simple console app that crawls through the one huge class and returns a list of all methods with the number of times each method was referenced. 我想编写一个简单的控制台应用程序,它可以遍历一个巨大的类,并返回所有方法的列表,其中包含每个方法的引用次数。 By my estimates there are well over 1000 methods, so doing this by hand would take a while. 根据我的估计,有超过1000种方法,所以手动这样做需要一段时间。

Are there classes within the .NET framework that I can use to examine this code? 我可以使用.NET框架中的类来检查此代码吗? Or any other usefull tools that may help identify methods that are declared but never referenced? 或者任何其他有用的工具,可以帮助识别声明但从未引用的方法?

(Side question: Has anyone else ever seen a C# app like this, one reeeealy big class? It's more or less one huge procedural process, I know this is the first I've seen, at least of this size.) (旁边的问题:有没有其他人见过像这样的C#应用​​程序,一个大的类?它或多或少是一个巨大的程序过程,我知道这是我见过的第一个,至少是这个大小。)

You could try to use NDepend if you just need to extract some stats about your class. 如果您只需要提取有关您班级的一些统计数据,您可以尝试使用NDepend Note that this tool relies on Mono.Cecil internally to inspect assemblies. 请注意,此工具在内部依赖Mono.Cecil来检查程序集。

Download the free trial of Resharper. 下载Resharper的免费试用版 Use the Resharper->Search->Find Usages in File (Ctrl-Shift-F7) to have all usages highlighted. 使用Resharper-> Search->在文件中查找用法(Ctrl-Shift-F7)可以突出显示所有用法。 Also, a count will appear in the status bar. 此外,状态栏中将显示计数。 If you want to search across multiple files, you can do that too using Ctrl-Alt-F7. 如果要搜索多个文件,也可以使用Ctrl-Alt-F7进行搜索。

If you don't like that, do text search for the function name in Visual Studio (Ctrl-Shift-F), this should tell you how many occurrences were found in the solution, and where they are. 如果您不喜欢这样,请在Visual Studio中搜索函数名称(Ctrl-Shift-F),这应该告诉您在解决方案中找到了多少次事件,以及它们的位置。

To complete the Romain Verdier answer, lets dig a bit into what NDepend can bring to you here. 要完成Romain Verdier的答案,让我们深入了解NDepend可以为您带来什么。 ( Disclaimer: I am a developer of the NDepend team ) 免责声明:我是NDepend团队的开发人员

NDepend lets query your .NET code with some LINQ queries. NDepend允许使用一些LINQ查询来查询.NET代码。 Knowing which methods call and is called by which others, is as simple as writing the following LINQ query: 知道哪些方法调用和被其他方法调用,就像编写以下LINQ查询一样简单:

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

The result of this query is presented in a way that makes easy to browse callers and callees (and its 100% integrated into Visual Studio). 此查询的结果以易于浏览调用者和被调用者(以及它100%集成到Visual Studio中)的方式呈现。

NDepend方法调用者和被调用者


There are many other NDepend capabilities that can help you. 还有许多其他NDepend功能可以帮助您。 For example you can right click a method in Visual Studio > NDepend > Select methods... > that are using me (directly or indirectly) ... 例如,您可以右键单击Visual Studio> NDepend> Select methods ...>中使用我的方法(直接或间接) ...

NDepend Visual Studio方法右键单击

The following code query is generated... 生成以下代码查询...

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

... which matches direct and indirect callers, with the depth of calls (1 means direct caller, 2 means caller of direct callers and so on). ...与直接和间接呼叫者匹配,具有呼叫深度(1表示直接呼叫者,2表示直接呼叫者的呼叫者,等等)。

NDepend间接方法调用者

And then by clicking the button Export to Graph , you get a call graph of your pivot method (of course it could be the other way around, ie method called directly or indirectly by a particular pivot method). 然后通过单击“ 导出到图形 ”按钮,您将获得您的pivot方法的调用图(当然,它可能是相反的方式,即通过特定的pivot方法直接或间接调用的方法)。

NDepend调用图

我不认为你想自己写这个 - 只需购买NDepend并使用它的代码查询语言

The Analyzer window in Reflector can show you where a method is called (Used By). Reflector中的Analyzer窗口可以显示调用方法的位置(Used By)。
Sounds like it would take a very long time to get the information that way though. 听起来好像需要很长时间来获取信息。
You might look at the API that Reflector provides for writing add-ins and see if you can get the grunt work of the analysis that way. 您可以查看Reflector为编写加载项提供的API,看看您是否可以通过这种方式获得分析的繁琐工作。 I would expect that the source code for the code metrics add-in could tell you a bit about how to get information about methods from the reflector API. 我希望代码度量标准加载项的源代码可以告诉您如何从反射器API获取有关方法的信息。

Edit: Also the code model viewer add-in for Reflector could help too. 编辑:Reflector的代码模型查看器加载项也可以提供帮助。 It's a good way to explore the Reflector API. 这是探索Reflector API的好方法。

FXCop has a rule that will identify unused private methods. FXCop有一条规则可以识别未使用的私有方法。 So you could mark all the methods private and have it generate a list. 因此,您可以将所有方法标记为私有,并让它生成列表。

FXCop also has a language if you wanted to get fancier http://www.binarycoder.net/fxcop/ FXCop也有一种语言,如果你想获得更多的发烧友http://www.binarycoder.net/fxcop/

If you don't want to shell out for NDepend, since it sounds like there is just a single class in a single assembly - comment out the methods and compile. 如果你不想为NDepend外壳,因为它听起来像一个程序集中只有一个类 - 注释掉方法并编译。 If it compiles, delete them - you aren't going to have any inheritance issues, virtual methods or anything like that. 如果它编译,删除它们 - 你不会有任何继承问题,虚拟方法或类似的东西。 I know it sounds primitive, but sometimes refactoring is just grunt work like this. 我知道这听起来很原始,但有时重构只是像这样的笨拙的工作。 This is kind of assuming you have unit tests you run after each build until you've got the code cleaned up (Red/Green/Refactor). 这是假设您在每次构建之后运行的单元测试,直到您清除了代码(红色/绿色/重构)。

I don't know of anything that's built to handle this specific case, but you could use Mono.Cecil. 我不知道为处理这个特定情况而构建的任何东西,但你可以使用Mono.Cecil。 Reflect the assemblies and then count references in the IL. 反映程序集,然后计算IL中的引用。 Shouldn't be too tough. 不应该太强硬。

There is no easy tool to do that in .NET framework itself. .NET框架本身没有简单的工具可以做到这一点。 However I don't think you really need a list of unused methods at once. 但是我不认为你真的需要一次未使用的方法列表。 As I see it, you'll just go through the code and for each method you'll check if it's unused and then delete it if so. 在我看来,你只需要查看代码,然后为每个方法检查它是否未使用,然后删除它。 I'd use Visual Studio "Find References" command to do that. 我使用Visual Studio“查找引用”命令来执行此操作。 Alternatively you can use Resharper with its "Analize" window. 或者,您可以使用Resharper及其“Analize”窗口。 Or you can just use Visual Studio code analysis tool to find all unused private methods. 或者您可以使用Visual Studio代码分析工具查找所有未使用的私有方法。

Try having the compiler emit assembler files, as in x86 instructions, not .NET assemblies. 尝试让编译器发出汇编程序文件,如x86指令,而不是.NET程序集。

Why? 为什么? Because it's much easier to parse assembler code than it is C# code or .NET assemblies. 因为解析汇编代码要比C#代码或.NET程序集容易得多。

For instance, a function/method declaration looks something like this: 例如,函数/方法声明看起来像这样:

    .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

and function/method references will look something like this: 和函数/方法引用将如下所示:

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

When you see "create_secure_tmpfile:" you know you have a function/method declaration, and when you see "call create_secure_tmpfile" you know you have a function/method reference. 当您看到“create_secure_tmpfile:”时,您知道您有一个函数/方法声明,当您看到“call create_secure_tmpfile”时,您知道您有一个函数/方法引用。 This may be good enough for your purposes, but if not it's just a few more steps before you can generate a very cute call-tree for your entire application. 这对于您的目的来说可能已经足够了,但如果不是这样,那么在为整个应用程序生成一个非常可爱的调用树之前,还需要几个步骤。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM