关于logger.BeginScope代码优化建议的等效性与副作用疑问
日志Scope优化写法的逻辑一致性与副作用分析
原代码
public async Task HandleAsync(Func<Task> next, CancellationToken cancellationToken) { using (var scope = logger.BeginScope("myName")) { await next(); } }
JetBrains Rider给出的两次优化建议
第一次优化
public async Task HandleAsync(Func<Task> next, CancellationToken cancellationToken) { using (_ = logger.BeginScope("myName")) { await next(); } }
第二次优化
public async Task HandleAsync(Func<Task> next, CancellationToken cancellationToken) { logger.BeginScope("myName"); await next(); }
问题
我想了解using (_ = logger.BeginScope("myName"))或最后一种写法是否与原代码逻辑完全一致,应用这些优化建议是否会产生不良副作用?
分析解答
关于using (_ = logger.BeginScope("myName"))
这种写法和原代码逻辑完全一致,没有不良副作用。
这是C# 8.0引入的丢弃变量版using声明语法,本质和var scope的写法没有区别:编译器会自动在using块结束时调用返回对象的IDisposable.Dispose()方法,不管next()是同步完成还是异步完成,都会确保日志Scope在next()执行完毕后才被正确释放,保证next()执行过程中的所有日志都能带上该Scope的上下文标记。只是因为代码里不需要用到这个变量的名称,用丢弃符号_让代码更简洁。
关于去掉using的写法
这种写法和原代码逻辑完全不一致,会产生严重的不良副作用。logger.BeginScope()返回的对象实现了IDisposable接口,原代码的using语句就是为了强制保证该对象在next()执行完成(包括异步流程结束)后才被释放,从而维持日志Scope的生命周期与next()的执行周期一致。
去掉using后,这个IDisposable对象会成为无引用的临时对象,会被CLR的垃圾回收器在不确定的时机回收并调用Dispose(),大概率会在next()还在异步执行时就提前释放日志Scope,导致后续next()流程中的日志丢失该Scope上下文,完全违背了原本要给next()全程添加日志Scope的设计意图。
总结
- 第一种优化(丢弃变量的using写法)可以放心使用,属于语法层面的简洁优化,逻辑等价于原代码;
- 第二种优化(去掉using)绝对不能应用,会破坏日志Scope的生命周期,引发日志上下文错误。
内容的提问来源于stack exchange,提问作者GokcenG
相关产品推荐
相关产品推荐

