在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
相关产品推荐
相关产品推荐

