我有一个 C# 项目,我需要为它找到不直接或间接从任何其他公共方法调用的所有私有方法。
此外,对于从公共方法调用的每个私有方法,我需要知道它是哪个公共方法。然后我将确定该方法是否真的是从该类的客户端调用的,如果不是,我将能够删除它。
过去,我使用 Lutz Rorder 的代码,它是 Reflector 的基础——它可以选择分析 IL 代码并在其之上提供对象模型。我现在找不到此代码。
有什么建议吗?也许指向那个 Lutz Rorder 代码?
萨尔
您应该查看.Net 的 Nitriq 静态代码分析- 他们有一个免费的社区版,并且他们的完整许可非常合理。
正如 Thomas 所指出的,工具NDepend可以帮助您在 .NET 代码库中找到未使用的代码。免责声明:我是这个工具的开发者之一。
NDepend 建议在 LINQ 查询 (CQLinq) 上编写代码规则。提出了大约200 条默认代码规则,其中 3 条专用于未使用/死代码检测:
NDepend 集成在 Visual Studio 中,因此可以在 IDE 中检查/浏览/编辑这些规则。该工具还可以集成到您的 CI 流程中,它可以构建报告,显示违反规则和罪魁祸首代码元素。
如果你点击上面这3个链接查看这些规则的源代码,你会发现关于类型和方法的部分有点复杂。这是因为它们不仅检测未使用的类型和方法,还检测仅由未使用的死类型和方法(递归)使用的类型和方法。
这是静态分析,因此规则名称中的前缀可能是。如果仅通过反射使用代码元素,则这些规则可能会将其视为未使用,但事实并非如此。
除了使用这 3 条规则之外,我还建议通过测试来衡量代码覆盖率并争取完全覆盖。通常,您会看到测试无法覆盖的代码实际上是可以安全丢弃的未使用/死代码。这在不清楚代码分支是否可达的复杂算法中特别有用。
此外,对于从公共方法调用的每个私有方法,我需要知道它是哪个公共方法。
要获取此信息,使用 CQLinq 您只需编写:
from m in Application.Methods
where m.IsPrivate
let publicMethodsCallingMe = m.MethodsCallingMe.Where(m1 => m1.IsPublic)
where publicMethodsCallingMe.Count() > 0
select new { m, publicMethodsCallingMe }
查询结果将很容易浏览:
好吧,这样做的唯一方法(至少我知道的唯一方法......)意味着使用商业(但不是那么昂贵)工具,即NDepend。
除此之外,您还可以针对已编译的程序集编写类似 SQL 的查询,从而实现非常细粒度的分析。他们称之为 CQL,语法不言自明,NDepend 的 IntelliSense/自动完成支持以及一般帮助/文档都非常好。
(AFAIK 他们还提供了一个功能齐全的试用版,如果这对你有帮助的话......)
!
不确定它是否能满足所有这些需求,但 FXCop 会为您完成大部分这些需求,并且还会帮助制定大量编码标准。无论哪种方式,都值得在您的 c# 代码上运行。
反射器仍然具有此功能。只需右键单击该方法,然后单击分析。按照树视图中的链接。
你没有说你是否需要在代码中做,或者只是“视觉上”。
如果需要代码,我相信您可以使用 Reflector SDK。
我也许可以使用 PowerCommands for Reflector 插件 ( http://powercommands.codeplex.com ) 中的查询编辑器来帮助你解决这个问题……我会看看今晚我能想出什么,然后告诉你明天。
否则,您也可以使用 CCI 或 Cecil 编写一个快速应用程序。
询问:
from a in AssemblyManager.Assemblies.Cast<IAssembly>()
where a.Name != "mscorlib"
&& !a.Name.Contains("System")
from m in a.Modules.Cast<IModule>()
from t in m.Types.Cast<ITypeDeclaration>()
from mt in t.Methods.Cast<IMethodDeclaration>()
where mt.Visibility == MethodVisibility.Public
&& !mt.RuntimeSpecialName
&& !mt.SpecialName
&& mt.Body is IMethodBody
from i in ((IMethodBody)mt.Body).Instructions.Cast<IInstruction>()
where i != null
&& i.Value != null
&& i.Value is IMethodReference
&& ((IMethodReference)i.Value).Resolve() != null
&& ((IMethodReference)i.Value).Resolve().Visibility == MethodVisibility.Private
select new {
CallingMethod=t.Namespace + "." + t.Name + "." + mt.Name,
PrivateReferencedMethod=((ITypeReference)((IMemberReference)((IMethodReference)i.Value).Resolve()).DeclaringType).Namespace + "."
+ ((ITypeReference)((IMemberReference)((IMethodReference)i.Value).Resolve()).DeclaringType).Name + "."
+ ((IMethodReference)i.Value).ToString()
}