在 Microsoft 的依赖注入中获取开放式通用服务

Bru*_*ell 5 c# generics dependency-injection asp.net-core

假设我们有以下服务:

interface IService { }
interface IService<T> : IService {
    T Get();
}
Run Code Online (Sandbox Code Playgroud)

在 ASP.Net-Core 中,在我们注册了一些不同的实现后,T我们可以获得所有注册的服务,如下所示:

IEnumerable<IService> services = serviceProvider.GetServices<IService>();
Run Code Online (Sandbox Code Playgroud)

现在,因为我需要从其他接口访问泛型类型参数,但这不是一个选项。如何在IService<T>不丢失泛型类型的情况下检索 的所有实现?就像是:

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.
}
Run Code Online (Sandbox Code Playgroud)

所有这些都应该有一个相当不错的表现。

Bru*_*ell 5

根据@JeroenMostert的有用评论,我发现了一种完全可以做我想做的事情的方法。正如他指出的,由于我们在编译时不知道通用参数类型,因此我们无法静态绑定该方法调用。我们需要的是后期绑定

反射是一种后期绑定,但有一个更好的解决方案: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);
}
Run Code Online (Sandbox Code Playgroud)

强制转换dynamic会将方法绑定推迟到运行时,当实际类型s已知时。然后它将寻找最合适的重载(如果没有,则会抛出异常)。与使用反射相比,这种方法有一些优点:

  • 我们不必关心缓存。DLR(动态语言运行时)将为我们处理它。
  • 我们绑定较晚,但获得尽可能多的静态分析。例如,所有其他参数类型都会被检查,并且在无效时可能会导致编译时错误。
  • 它更短,更容易写。

您可以在此处阅读一篇关于此方法及其与反射的比较的优秀深入文章。