我是 log4net 和 log4j 用于记录消息的“格式”API 的忠实粉丝,这可以避免在未启用必要的日志级别时对参数调用 ToString() 的成本。
但有时我使用的一个或多个参数不是一个简单的对象,它需要以某种方式构造。例如,像这样:
logger.DebugFormat("Item {0} not found in {1}",
itemID,
string.Join(",", items.Select(i => <you get the idea>))
);
是否有一种技术可以封装第二个参数(Join 表达式),以便除非 DebugFormat 决定它应该执行(就像对第一个参数的 ToString 所做的那样),否则它不会被执行?
感觉 lambda 或 func 或其他东西应该能够在这里提供帮助,但我对 C# 相当陌生,我不太清楚它。
您可以创建扩展方法或包装类,但获得令人满意的语法并不容易,因为您希望明确声明一些参数(示例中的
itemID
),并且仅在必要时才解析一些参数。但是你不能将匿名函数作为 object
传递。相反,我会使用另一个不需要扩展方法或包装器的解决方案。像这样创建类:
public sealed class Delayed {
private readonly Lazy<object> _lazy;
public Delayed(Func<object> func) {
_lazy = new Lazy<object>(func, false);
}
public override string ToString() {
var result = _lazy.Value;
return result != null ? result.ToString() : "";
}
}
此函数接受在构造函数中返回对象的函数,并且在调用
ToString()
之前不会调用此函数,正如您所知,log4net 仅在必要时才会调用该函数(如果启用了此类调试级别)。然后像这样使用:
logger.DebugFormat("Item {0} not found in {1}",
itemID,
new Delayed(() => string.Join(",", items.Select(i => <you get the idea>)))
);
我猜 log4net 设计此用例的方式是提出 Is{Level}Available() 方法,例如 IsDebugEnabled() 方法,仅当接收者记录器的级别实际上处于活动状态时,该方法才返回 True。 因此,您可以使用昂贵构造的参数保留您的 LogFormat() 调用,但只需将其括起来:
if (logger.IsDebugEnabled())
{
...
}