You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C#中如何获取所有开放泛型IEmpty<>的服务实现?

批量获取泛型接口IEmpty<>所有实现服务的解决方案

问题场景

定义了如下泛型接口及实现类:

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

这些服务可正常通过构造函数显式注入:

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

但尝试通过开放泛型类型批量解析时:

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

会抛出NotSupportedException(无法创建开放类型的数组),需要找到批量获取所有IEmpty<>实现服务的可行方法。

需求说明

  • 每个服务仅注册一次(遵循DRY原则)
  • 无需解析器模式或命名依赖,可在构造函数显式注入服务
  • 启动后加载所有实现类验证特定属性,避免反射遍历所有程序集

使用的NuGet包

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

解决方案

步骤1:添加非泛型基接口

让泛型接口IEmpty<T>继承一个非泛型标记接口IEmpty,所有实现类会间接实现该非泛型接口:

public interface IEmpty { }
public interface IEmpty<T> : IEmpty { }

步骤2:封装注册扩展方法

创建扩展方法,一次性完成泛型接口和非泛型接口的注册,避免重复代码:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddEmptyService<T>(this IServiceCollection services) 
        where T : class, IEmpty<T>
    {
        // 注册泛型接口,保证构造函数注入正常工作
        services.AddTransient<IEmpty<T>, T>();
        // 注册非泛型接口,用于批量获取所有实现
        services.AddTransient<IEmpty>(sp => sp.GetRequiredService<IEmpty<T>>());
        return services;
    }
}

步骤3:修改服务注册代码

使用扩展方法注册所有实现类,遵循DRY原则:

.ConfigureServices((context, services) =>
{
    services.AddEmptyService<Empty1>();
    services.AddEmptyService<Empty2>();
    services.AddEmptyService<EmptyN>();
    services.AddTransient<NewClass1>();
});

步骤4:批量获取所有实现

现在可以通过非泛型接口IEmpty批量获取所有实现类:

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

完整验证代码(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<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.AddEmptyService<Empty1>();
                services.AddEmptyService<Empty2>();
                services.AddEmptyService<EmptyN>();
                services.AddTransient<NewClass1>();
            });
    return hostBuilder;
}

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddEmptyService<T>(this IServiceCollection services) 
        where T : class, IEmpty<T>
    {
        services.AddTransient<IEmpty<T>, T>();
        services.AddTransient<IEmpty>(sp => sp.GetRequiredService<IEmpty<T>>());
        return services;
    }
}

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

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

内容的提问来源于stack exchange,提问作者Ingo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:15:43