You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 14:57:02