如何在C# Winform/控制台应用或类方法中实现类似Action Filter的日志功能
无需第三方包的方法执行节点记录实现方案
以下4种都是.NET生态内置的实现方案,无需引入任何第三方NuGet包:
方案1:通用委托/泛型包装(最简实现,无侵入性要求)
适合零散方法的临时埋点,不需要修改原有类结构,直接封装通用执行逻辑即可:
// 带返回值的通用执行包装方法 public static T ExecuteWithLog<T>(Func<T> func, string methodName = "") { Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 方法 {methodName} 开始执行"); try { var result = func.Invoke(); Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 方法 {methodName} 执行完成"); return result; } catch (Exception ex) { Console.WriteLine($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] 方法 {methodName} 执行异常:{ex.Message}"); throw; } } // 无返回值的重载 public static void ExecuteWithLog(Action action, string methodName = "") { ExecuteWithLog(() => { action.Invoke(); return 0; }, methodName); }
使用方式:
// 调用原有方法时用包装器包裹即可 ExecuteWithLog(() => YourBusinessMethod(param1, param2), nameof(YourBusinessMethod));
方案2:装饰器模式(适合依赖注入场景,全量埋点)
如果项目已经使用依赖注入,用装饰器模式可以对某个接口的所有实现类统一埋点,不需要修改原有业务代码:
- 第一步:定义业务接口和原有实现类
- 第二步:编写同接口的装饰器类,内部调用原实现的前后插入日志逻辑
- 第三步:在DI容器中注册装饰器替换原有实现
代码示例:
// 原有业务接口 public interface IOrderService { void CreateOrder(OrderInfo info); } // 原有业务实现 public class OrderService : IOrderService { public void CreateOrder(OrderInfo info) { // 原有业务逻辑 } } // 日志装饰器 public class LogOrderServiceDecorator : IOrderService { private readonly IOrderService _innerService; public LogOrderServiceDecorator(IOrderService innerService) => _innerService = innerService; public void CreateOrder(OrderInfo info) { Console.WriteLine("CreateOrder方法开始执行"); _innerService.CreateOrder(info); Console.WriteLine("CreateOrder方法执行完成"); } }
方案3:内置DispatchProxy实现AOP(适合批量埋点,无需逐个写装饰器)
.NET Core 3.0及以上版本、.NET 5+ 都内置了DispatchProxy类,可以实现运行时代理,批量为接口的所有方法统一添加日志逻辑,不需要为每个接口单独写装饰器:
public class LogProxy : DispatchProxy { public object Target { get; set; } protected override object Invoke(MethodInfo targetMethod, object[] args) { Console.WriteLine($"方法 {targetMethod.Name} 开始执行"); try { var result = targetMethod.Invoke(Target, args); Console.WriteLine($"方法 {targetMethod.Name} 执行完成"); return result; } catch (Exception ex) { Console.WriteLine($"方法 {targetMethod.Name} 执行异常:{ex.Message}"); throw; } } // 静态创建代理的工具方法 public static T Create<T>(T target) where T : class { var proxy = Create<T, LogProxy>() as LogProxy; proxy.Target = target; return proxy as T; } }
使用方式:
IOrderService orderService = new OrderService(); // 生成带日志的代理对象 var proxiedService = LogProxy.Create(orderService); // 调用代理对象的所有方法都会自动触发日志逻辑 proxiedService.CreateOrder(new OrderInfo());
方案4:ContextBoundObject(仅适用于.NET Framework场景)
如果是.NET Framework的项目,还可以通过继承ContextBoundObject+自定义属性的方式实现类级别的全局埋点,缺点是要求业务类必须继承自ContextBoundObject,侵入性较强,性能也相对较低。
内容的提问来源于stack exchange,提问作者kailash
相关产品推荐
相关产品推荐

