Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/301.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在Microsofts依赖项注入中获取开放式通用服务_C#_Generics_Asp.net Core_Dependency Injection - Fatal编程技术网

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);
    }