如何为C#项目所有函数批量添加Stopwatch计时功能
批量给C#函数添加计时的几种实用方法
1. 使用AOP框架(PostSharp)
这是最省心的方案,通过特性就能批量给类或方法注入计时逻辑,无需手动修改每个函数。
首先安装PostSharp NuGet包,然后定义一个计时特性:
[Serializable] public class TimingAttribute : OnMethodBoundaryAspect { private Stopwatch _stopwatch; public override void OnEntry(MethodExecutionArgs args) { _stopwatch = Stopwatch.StartNew(); } public override void OnExit(MethodExecutionArgs args) { _stopwatch.Stop(); Console.WriteLine($"方法 {args.Method.Name} 执行耗时: {_stopwatch.ElapsedMilliseconds}ms"); } }
然后直接给整个类或者单个方法加特性,编译后PostSharp会自动把计时逻辑织入:
// 给整个类的所有方法加计时 [Timing] public class BusinessService { public void ProcessOrder() { // 业务逻辑 } public string GetUserInfo(int userId) { // 业务逻辑 return "user info"; } }
2. 装饰器模式(适合接口驱动的项目)
如果你的项目是基于接口设计的,可以用装饰器包装每个实现类,统一添加计时逻辑,不用修改原业务代码。
比如先定义接口:
public interface IOrderService { void ProcessOrder(int orderId); } public class OrderService : IOrderService { public void ProcessOrder(int orderId) { // 原始业务逻辑 Thread.Sleep(100); } }
然后写装饰器类:
public class TimingOrderServiceDecorator : IOrderService { private readonly IOrderService _innerService; public TimingOrderServiceDecorator(IOrderService innerService) { _innerService = innerService; } public void ProcessOrder(int orderId) { var stopwatch = Stopwatch.StartNew(); _innerService.ProcessOrder(orderId); stopwatch.Stop(); Console.WriteLine($"ProcessOrder({orderId}) 耗时: {stopwatch.ElapsedMilliseconds}ms"); } }
使用的时候用装饰器包装原实例:
var orderService = new TimingOrderServiceDecorator(new OrderService()); orderService.ProcessOrder(123);
如果接口很多,可以结合依赖注入容器(比如Autofac、Microsoft DI)批量注册装饰器,不用手动每个都包装。
3. 用Roslyn批量修改代码
如果需要直接给源代码添加计时逻辑,可以用Roslyn(.NET编译器平台)写个小工具,遍历项目中的所有方法,自动插入Stopwatch代码。
示例代码(简化版):
var workspace = MSBuildWorkspace.Create(); var project = await workspace.OpenProjectAsync(@"YourProject.csproj"); foreach (var document in project.Documents) { var syntaxTree = await document.GetSyntaxTreeAsync(); var root = await syntaxTree.GetRootAsync(); // 遍历所有方法声明 var methods = root.DescendantNodes().OfType<MethodDeclarationSyntax>(); var newRoot = root.ReplaceNodes(methods, (original, _) => { // 创建Stopwatch的变量声明和计时代码 var stopwatchVar = SyntaxFactory.LocalDeclarationStatement( SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("var")) .AddVariables(SyntaxFactory.VariableDeclarator("stopwatch") .WithInitializer(SyntaxFactory.EqualsValueClause( SyntaxFactory.ParseExpression("Stopwatch.StartNew()"))))); var stopCode = SyntaxFactory.ExpressionStatement( SyntaxFactory.ParseExpression("stopwatch.Stop()")); var logCode = SyntaxFactory.ExpressionStatement( SyntaxFactory.ParseExpression($"Console.WriteLine(\"方法 {original.Identifier.Text} 耗时: \" + stopwatch.ElapsedMilliseconds + \"ms\")")); // 把计时代码插入方法体开头和结尾 var newBody = original.Body .WithStatements(SyntaxFactory.List( new[] { stopwatchVar }.Concat(original.Body.Statements).Concat(new[] { stopCode, logCode }))); return original.WithBody(newBody); }); // 保存修改后的文档 await document.WithSyntaxRoot(newRoot).SaveAsync(); }
这个工具可以一次性给项目里的所有方法加上计时代码,注意先备份代码再运行。
4. 利用.NET的Activity/DiagnosticSource
这种方式不需要修改业务代码,通过监听诊断事件来收集方法执行时间,适合非侵入式的计时。
首先在方法中使用Activity:
public void ProcessData() { using var activity = new Activity("ProcessData").Start(); // 业务逻辑 }
然后订阅DiagnosticListener的事件来获取计时信息:
DiagnosticListener.AllListeners.Subscribe(listener => { if (listener.Name == "YourCustomListener") { listener.SubscribeWithAdapter(new ActivityListener()); } }); public class ActivityListener { [DiagnosticName("Activity.Start")] public void OnActivityStart(Activity activity) { // 可记录开始时间戳 } [DiagnosticName("Activity.Stop")] public void OnActivityStop(Activity activity) { Console.WriteLine($"Activity {activity.DisplayName} 耗时: {activity.Duration.TotalMilliseconds}ms"); } }
内容的提问来源于stack exchange,提问作者Gökhan AKSOY
相关产品推荐
相关产品推荐

