C#工厂模式下如何为各Parser类注入对应ILogger<T>实例
问题解答
一、从工厂为每个Parser传递对应分类日志至BaseParser
要实现这一点,核心是让工厂通过ILoggerFactory创建对应Parser类型的日志实例,再传递给Parser的构造函数,最终传入BaseParser。具体步骤和代码示例如下:
1. 调整基类与子类的构造函数
让BaseParser接受非泛型ILogger作为依赖,子类明确声明需要对应自身类型的泛型ILogger<T>,并将其传递给父类:
// 抽象基类 public abstract class BaseParser { protected readonly ILogger Logger; protected BaseParser(ILogger logger) { Logger = logger ?? throw new ArgumentNullException(nameof(logger)); } public abstract void Parse(); } // FileParser子类 public class FileParser : BaseParser { public FileParser(ILogger<FileParser> logger) : base(logger) { } public override void Parse() { Logger.LogInformation("执行文件解析逻辑"); } } // FileParser的具体实现Parser1 public class Parser1 : FileParser { public Parser1(ILogger<Parser1> logger) : base(logger) { } public override void Parse() { Logger.LogInformation("执行Parser1的文件解析逻辑"); } }
2. 改造工厂类,注入ILoggerFactory
将工厂的依赖从ILogger<Factory>改为ILoggerFactory,这样就能为每个Parser类型创建对应的分类日志实例:
public class ParserFactory { private readonly ILoggerFactory _loggerFactory; // 注入ILoggerFactory而非具体的ILogger<Factory> public ParserFactory(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); } // 类型安全的具体Parser创建方法(推荐) public Parser1 CreateParser1() { var logger = _loggerFactory.CreateLogger<Parser1>(); return new Parser1(logger); } // 通用创建方法(适合多种Parser类型,需反射) public T CreateParser<T>() where T : BaseParser { var logger = _loggerFactory.CreateLogger<T>(); return (T)Activator.CreateInstance(typeof(T), logger); } }
通过这种方式,BaseParser中使用的Logger会是对应具体子类的分类日志,日志输出时会自动带上子类的类型名称,便于排查问题。
二、注入ILoggerFactory到Parser中创建日志是否属于不良实践
结论:这属于不推荐的不良实践,原因如下:
- 违背依赖注入原则:依赖注入要求依赖由外部提供,而非组件自行创建。Parser自行通过ILoggerFactory创建日志,相当于把依赖创建的责任揽到自己身上,破坏了DI的解耦理念。
- 降低可测试性:如果Parser内部创建日志,测试时需要Mock ILoggerFactory才能替换日志实现,相比直接注入ILogger
,测试复杂度更高。 - 隐藏依赖:Parser的构造函数没有显式声明对日志的依赖,其他开发者阅读代码时无法直观发现该组件需要日志功能。
- 不符合.NET日志最佳实践:.NET官方推荐直接注入
ILogger<T>,框架会自动处理日志分类、上下文传递等细节,手动创建容易遗漏配置或导致日志上下文丢失。
仅在极少数特殊场景下(比如运行时动态生成的Parser类型,无法提前在DI容器中注册),注入ILoggerFactory才是必要的折中方案。
内容的提问来源于stack exchange,提问作者Gicu Mironica
相关产品推荐
相关产品推荐

