C# 在Microsofts依赖项注入中获取开放式通用服务
假设我们有以下服务:C# 在Microsofts依赖项注入中获取开放式通用服务,c#,generics,asp.net-core,dependency-injection,C#,Generics,Asp.net Core,Dependency Injection,假设我们有以下服务: interface IService { } interface IService<T> : IService { T Get(); } 现在,因为我需要从另一个不是选项的接口访问泛型类型参数如何检索iSeries设备的所有实现而不丢失泛型类型?类似于: IEnumerable<IService<T>> services = serviceProvider.GetServices<IService<T>>
interface IService { }
interface IService<T> : IService {
T Get();
}
现在,因为我需要从另一个不是选项的接口访问泛型类型参数如何检索iSeries设备的所有实现而不丢失泛型类型?
类似于:
IEnumerable<IService<T>> services = serviceProvider.GetServices<IService<T>>();
foreach (var s in services) {
Method(s);
}
// Here we have a generic method I don't have control over.
// I want to call the method for each `T` registered in DI
void Method<T>(IService<T> service) {
Type t = typeof(T); // This here will resolve to the actual type, different in each call. Not object or whatever less derived.
}
IEnumerable services=serviceProvider.GetServices();
foreach(服务中的var s){
方法(s);
}
//这里我们有一个我无法控制的通用方法。
//我想为在DI中注册的每个'T'调用该方法
无效方法(iSeries服务){
Type t=typeof(t);//这里将解析为实际类型,在每次调用中不同。而不是对象或派生较少的任何类型。
}
所有这些都应该有一个不错的性能。我可以想到两个选项:
iSeries设备
,并过滤掉不兼容的类型:
serviceProvider.GetServices<IService>().OfType<IService<T>>();
此外,由于您似乎在使用不同的容器,您可能可以使用自动注册(也称为组装扫描)来简化样板文件。根据我提供的有用意见,我发现了一种方法,可以完全按照我的要求来做。正如他指出的,因为我们在编译时不知道泛型参数类型,所以我们不能静态绑定该方法调用。我们需要的是 反射是一种后期绑定,但有更好的解决方案:
dynamic
答案中的例子是:
IEnumerable<IService> services = serviceProvider.GetServices<IService>();
foreach (var s in services) {
Method((dynamic)s);
}
void Method<T>(IService<T> service) {
// This here will resolve to the actual type, different in each call. Not object or whatever less derived.
Type t = typeof(T);
}
IEnumerable services=serviceProvider.GetServices();
foreach(服务中的var s){
方法((动态)s);
}
无效方法(iSeries服务){
//这里将解析为实际类型,每个调用都不同。而不是对象或派生较少的任何类型。
类型t=类型(t);
}
转换为dynamic
将推迟方法绑定,直到运行时s
的实际类型已知。然后它将寻找最合适的重载(如果没有,将抛出异常)。这种方法与使用反射相比有一些优点:
- 我们不必关心缓存。DLR(动态语言运行库)将为我们处理它
- 我们绑定得很晚,但得到尽可能多的静态分析。例如,检查所有其他参数类型,如果无效,则可能导致编译时错误
- 它比较短,也比较容易写
您可以阅读一篇非常深入的文章,介绍这种方法以及它与反射的比较。serviceProvider.GetServices是否也返回所有
iSeries
实例?如果是这样的话,这是您的明显问题(您可以使用Type.GetGenericArguments
并直接传递此类型,或者使用.MakeGenericType/.MakeGenericMethod
),如果不是这样的话,这个问题似乎无法解决。@JeroenMostert在我的特定情况下,它确实可以解决(在服务提供商后面使用Autofac)。这可能是我的方法,但我想检查是否可以使用纯泛型而不使用反射。如果可能的话,那么就完全删除非泛型接口IService
。不进行任何反射是不可能的,因为泛型是编译时的,像GetServices
这样的开放式方法只会生成运行时类型。您可以应用一系列技巧来加速、缓存或预编译反射,但不能完全消除它。@Jeroenmoster您能给我一些有关预编译反射的参考资料吗?从未听说过它,而且它似乎是相互测试的。这基本上涉及到使用反射.Emit
和/或表达式树在运行时发出特定于类型的代码并缓存它。如果代码只运行一次(因为您对代码进行了反射和编译),那么这显然比反射还要慢,但是当您开始在紧密循环中运行时,它会得到回报。查看任何ORM的内部(如Dapper或Entity Framework,甚至AutoMapper)以了解更多细节。在这种情况下,我假设你只看过一次收集,所以它真的不会有回报。
services.AddScoped<IService, FooService>();
services.AddScoped<IService, BarService1>();
services.AddScoped<IService, BarService2>();
services.AddScoped<IService<Foo>, FooService>();
services.AddScoped<IService<Bar>, BarService1>();
services.AddScoped<IService<Bar>, BarService2>();
serviceProvider.GetServices<IService<Bar>>(); // returns 2 services
serviceProvider.GetServices<IService>(); // returns 3 services
services.AddScoped<FooService>();
services.AddScoped<BarService1>();
services.AddScoped<BarService2>();
services.AddScoped<IService>(c => c.GetRequiredService<FooService>());
services.AddScoped<IService>(c => c.GetRequiredService<BarService1>());
services.AddScoped<IService>(c => c.GetRequiredService<BarService2>());
services.AddScoped<IService<Foo>>(c => c.GetRequiredService<FooService>());
services.AddScoped<IService<Bar>>(c => c.GetRequiredService<BarService1>());
services.AddScoped<IService<Bar>>(c => c.GetRequiredService<BarService2>());
IEnumerable<IService> services = serviceProvider.GetServices<IService>();
foreach (var s in services) {
Method((dynamic)s);
}
void Method<T>(IService<T> service) {
// This here will resolve to the actual type, different in each call. Not object or whatever less derived.
Type t = typeof(T);
}