c# - 我可以在 C# 中解析带有 'GetServices(typeof(IEmpty<>))' 的开放类型泛型服务集合吗?

标签 c# .net .net-core dependency-injection inversion-of-control

具有以下接口(interface)及其实现...

public interface IEmpty<T> { }
public class Empty1 : IEmpty<Empty1>{ }
public class Empty2 : IEmpty<Empty2>{ }
public class EmptyN : IEmpty<EmptyN>{ }

允许我注册它们并将它们显式注入(inject)到构造函数中

public class NewClass1 {
private IEmpty<Empty1> Empty;
    public NewClass1(IEmpty<Empty1> empty)
    {
        Empty = empty;
    }
    public string EmptyType => $"{Empty.GetType()}";
}

但是当我尝试立即解决“IEmpty<>”的所有实现时...

var allIEmpties = host.Services.GetServices(typeof(IEmpty<>));
allIEmpties.ToList().ForEach(empty => Console.WriteLine(empty.GetType()));

...代码的执行抛出了“NotSupportedException”(无法创建开放类型的数组),我有点理解,但让我想知道是否可以完成以及如何完成才能获得处理所有实现 IEmpty 的服务。

有人知道如何实现这一目标吗?


我让这项工作发挥作用的动机是

  1. 每个服务仅注册一次(DRY)
  2. 能够将服务显式注入(inject)构造函数,而不需要某些解析器模式或命名依赖项
  3. 启动后加载所有实现,以验证由于接口(interface)而需要实现的特定属性,而无需反射(reflection)性地搜索所有程序集,这是我的临时解决方案,但爬过一盒针来找到 2 个特定的针听起来是错误的,如果我可以有一个分类盒,让我可以直接取用我需要的针……

使用这些额外的 nuget 包:

  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Hosting
  • Microsoft.Extensions.Hosting.Abstractions

我在 LinqPad 中创建了这个概念验证片段,以防您想尝试一下:

void Main()
{
    var args = new List<string>();

    var host = CreateHostBuilder(args.ToArray()).Build();

    var newClass = host.Services.GetService<NewClass1>();
    Console.WriteLine(newClass.EmptyType);

    var oneEmpty = host.Services.GetService(typeof(IEmpty<Empty2>));
    Console.WriteLine(oneEmpty.GetType());

    var allIEmpties = host.Services.GetServices(typeof(IEmpty<>));
    allIEmpties.ToList().ForEach(empty => Console.WriteLine(empty.GetType()));
}

IHostBuilder CreateHostBuilder(string[] args)
{
    var hostBuilder = 
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, builder) => {
                builder.SetBasePath(Directory.GetCurrentDirectory());
            })
            .ConfigureServices((context, services) => {
                services.AddTransient<IEmpty<Empty1>, Empty1>();
                services.AddTransient<IEmpty<Empty2>, Empty2>();
                services.AddTransient<IEmpty<EmptyN>, EmptyN>();
                services.AddTransient<NewClass1>();
            });
    return hostBuilder;
}

public class NewClass1 {
    private IEmpty<Empty1> Empty;
    public NewClass1(IEmpty<Empty1> empty)
    {
        Empty = empty;
    }
    public string EmptyType => $"{Empty.GetType()}";
}

public interface IEmpty<T> {}
public class Empty1 : IEmpty<Empty1>{ }
public class Empty2 : IEmpty<Empty2>{ }
public class EmptyN : IEmpty<EmptyN>{ }

最佳答案

通过调用 GetServices(typeof(IEmpty<>)) 根据其开放泛型定义解析泛型类型列表MS.DI 不支持。尽管技术上可行,但我所熟悉的 DI 容器中并没有真正支持此功能的。

有很多可能的方法可以解决您的问题。例如,您可以引入一个非泛型 IEmpty (标记)接口(interface) IEmpty<T>继承自。

您还可以使用反射浏览代码库,正如您已经提到的,或者您可以浏览 ServiceCollection 中的注册。获取所有注册IEmpty<T>注册。然后可以使用该列表来获取列表。例如:

var emptyTypes =
    from s in services
    where s.ServiceType.IsGenericType
    where s.ServiceType.GetGenericTypeDefinition() == typeof(IEmpty<>)
    select s.ServiceType;
    
foreach (Type emptyType in emptyTypes)
{
    var empty = host.Services.GetRequiredService(emptyType);
    Console.WriteLine(empty.GetType());
}

关于c# - 我可以在 C# 中解析带有 'GetServices(typeof(IEmpty<>))' 的开放类型泛型服务集合吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73645572/

相关文章:

c# - Appsettings.json 获取两个正在运行的程序的字符串

c# - 通过委托(delegate)的实例调用带有可选参数的方法

c# - 第二个端点不同 'behaviorConfiguration'

c# - 将文本从word文件复制到一个新的word

c# - 图表工具中使用了哪些设计模式?

c# - 是否可以静默运行 .NET Core 控制台应用程序(隐藏控制台窗口)?

c# - 检测线程已在 C# .net 中运行?

c# - 获取C#中撇号之间的值

c# - 使用反射加载程序集时的内存管理

c# - AWS Lambda 无法识别 .net Core 上的文化