如何通过IServiceProvider动态解析泛型IGenerator<T>服务以适配新增模型类型
解决方案:动态解析泛型生成器服务
你的痛点我太懂了——每次加新模型都要改GenerateModel的if判断,完全不符合开闭原则。其实核心问题是泛型类型参数需要编译时确定,但你现在只有运行时的Type变量,解决思路就是动态构造出对应的IGenerator<T>泛型类型,再从DI容器里获取实例。
步骤1:动态构造泛型服务类型
首先,你需要用typeof(IGenerator<>).MakeGenericType(modelType)来创建对应模型的泛型生成器类型,然后通过IServiceProvider获取这个类型的实例。
步骤2:调用Generate方法
因为获取到的实例是object类型,你需要用反射调用Generate方法,或者可以封装一个通用的扩展方法来简化操作。
完整实现代码
先看修改后的GenerateModel方法:
private readonly IServiceProvider _services; private IModel GenerateModel(Type modelType, string someInput) { // 1. 动态构造IGenerator<T>的泛型类型 var generatorType = typeof(IGenerator<>).MakeGenericType(modelType); // 2. 从DI容器获取生成器实例 var generator = _services.GetService(generatorType); if (generator == null) { throw new InvalidOperationException($"No generator registered for type {modelType.Name}"); } // 3. 反射调用Generate方法 var generateMethod = generatorType.GetMethod(nameof(IGenerator<IModel>.Generate)); if (generateMethod == null) { throw new MissingMethodException($"Generate method not found on {generatorType.Name}"); } var result = generateMethod.Invoke(generator, new object[] { someInput }); return result as IModel ?? throw new InvalidCastException($"Result from generator is not an IModel"); }
优化:封装通用扩展方法
为了避免每次都写反射的代码,你可以封装一个扩展方法,复用逻辑:
public static class ServiceProviderExtensions { public static TModel GenerateModel<TModel>(this IServiceProvider services, string someInput) where TModel : IModel { var generator = services.GetService<IGenerator<TModel>>(); if (generator == null) { throw new InvalidOperationException($"Generator for {typeof(TModel).Name} not registered"); } return generator.Generate(someInput); } // 针对Type变量的重载 public static IModel GenerateModel(this IServiceProvider services, Type modelType, string someInput) { var method = typeof(ServiceProviderExtensions) .GetMethod(nameof(GenerateModel), new[] { typeof(IServiceProvider), typeof(string) }) ?.MakeGenericMethod(modelType); if (method == null) { throw new InvalidOperationException($"Cannot generate model for type {modelType.Name}"); } return method.Invoke(null, new object[] { services, someInput }) as IModel ?? throw new InvalidCastException($"Result is not an IModel"); } }
这样你调用的时候就可以简化成:
// 用Type变量调用 var model = _services.GenerateModel(modelType, someInput); // 如果知道具体类型,还可以用泛型重载 var model1 = _services.GenerateModel<Model1>(someInput);
关键注意点:DI注册要正确
确保你在DI容器中正确注册了每个生成器:
// 在Startup.cs或者Program.cs里注册 services.AddScoped<IGenerator<Model1>, Model1Generator>(); services.AddScoped<IGenerator<Model2>, Model2Generator>(); // 新增模型时只需要加这一行,不用改GenerateModel方法!
这样每次新增模型,你只需要创建对应的Generator类,然后在DI里注册一下,完全不用修改原来的GenerateModel逻辑,完美符合开闭原则。
内容的提问来源于stack exchange,提问作者DirtyNative
相关产品推荐
相关产品推荐

