select 将注入哪个服务实现使用通用参数有意义吗?
Does it make sense to use generic parameters to select which service implementation will be injected?
我有一个叫做 ReportWorkflow<TReport>
的 class 调用一些对象来生成特定类型的报告,对象总是以相同的顺序调用,唯一真正的区别是实际的实现每个对象。
此 class 的构造函数如下所示:
public ReportWorkflow(... , IFileNameProvider<TReport> fileNameProvider)
ReportWorkflow<TReport>
是使用依赖注入构造的。
DI Container中同时注册了多个ReportWorkflow<TReport>
,如:
services.AddSingleton<IReportWorkflow, ReportWorkflow<HrReport>();
services.AddSingleton<IReportWorkflow, ReportWorkflow<AccountingReport>();
//More ReportWorkflows registrations...
为确保我为每个 ReportWorkflow<TReport>
获得正确的文件名提供程序,我已将通用参数 TReport
添加到接口 IFileNameProvider<TReport>
但此接口从不使用通用参数参数,它仅用于 'mark' 并将实现与另一个实现区分开来。
接口定义如下
public interface IFileNameProvider<TReport> {
string GetFileName();
}
一个实现只需用报告类型填充通用参数,它将被注册为:
services.AddSingleton<IFileNameProvider<HrReport>, HrFileNameProvider>();
services.AddSingleton<IFileNameProvider<AccountingReport>, AccountingFileNameProvider>();
这将确保当我尝试解析 ReportWorkflow<HrReport>
时,我将得到 HrFileNameProvider
,而当我尝试解析 ReportWorkflow<AccountingReport>
时,我将得到 AccountingFileNameProvider
。
'type markers' HrReport
或 AccountingReport
是其他对象实际使用和返回的 classes,确实包含一些信息。
使用通用参数的接口作为一种 'marker' 来区分一个实现与另一个实现是否有意义?如果是,这个图案有名字吗?
恭喜,您可能刚刚重新发明了 phantom types ;)
虽然它在 Haskell 和其他静态类型的函数式编程语言中是一件(古怪的)事情,但这里的问题也意味着它可能不是最 idiomatic 适用于 C# 的设计。
综上所述,在我看来,这是使 DI 容器与设计一起工作的解决方法。当 DI 容器挡道时,值得考虑它是否真的增加了价值,或者它是否是一种责任。
考虑扔掉容器,转而依赖 Pure DI。这可以使一切变得更简单,也可能使幻像类型的使用变得多余。
我有一个叫做 ReportWorkflow<TReport>
的 class 调用一些对象来生成特定类型的报告,对象总是以相同的顺序调用,唯一真正的区别是实际的实现每个对象。
此 class 的构造函数如下所示:
public ReportWorkflow(... , IFileNameProvider<TReport> fileNameProvider)
ReportWorkflow<TReport>
是使用依赖注入构造的。
DI Container中同时注册了多个ReportWorkflow<TReport>
,如:
services.AddSingleton<IReportWorkflow, ReportWorkflow<HrReport>();
services.AddSingleton<IReportWorkflow, ReportWorkflow<AccountingReport>();
//More ReportWorkflows registrations...
为确保我为每个 ReportWorkflow<TReport>
获得正确的文件名提供程序,我已将通用参数 TReport
添加到接口 IFileNameProvider<TReport>
但此接口从不使用通用参数参数,它仅用于 'mark' 并将实现与另一个实现区分开来。
接口定义如下
public interface IFileNameProvider<TReport> {
string GetFileName();
}
一个实现只需用报告类型填充通用参数,它将被注册为:
services.AddSingleton<IFileNameProvider<HrReport>, HrFileNameProvider>();
services.AddSingleton<IFileNameProvider<AccountingReport>, AccountingFileNameProvider>();
这将确保当我尝试解析 ReportWorkflow<HrReport>
时,我将得到 HrFileNameProvider
,而当我尝试解析 ReportWorkflow<AccountingReport>
时,我将得到 AccountingFileNameProvider
。
'type markers' HrReport
或 AccountingReport
是其他对象实际使用和返回的 classes,确实包含一些信息。
使用通用参数的接口作为一种 'marker' 来区分一个实现与另一个实现是否有意义?如果是,这个图案有名字吗?
恭喜,您可能刚刚重新发明了 phantom types ;)
虽然它在 Haskell 和其他静态类型的函数式编程语言中是一件(古怪的)事情,但这里的问题也意味着它可能不是最 idiomatic 适用于 C# 的设计。
综上所述,在我看来,这是使 DI 容器与设计一起工作的解决方法。当 DI 容器挡道时,值得考虑它是否真的增加了价值,或者它是否是一种责任。
考虑扔掉容器,转而依赖 Pure DI。这可以使一切变得更简单,也可能使幻像类型的使用变得多余。