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

.NET 6中泛型/非泛型服务继承基接口的注入解析优化问询

问题

在.NET 6环境下,我有一批均继承自同一非泛型接口IService的服务类,但部分服务并非直接继承该接口,而是通过中间泛型接口实现继承。

代码定义如下:

public interface IService { }   
public interface IServiceDirect {}
public class ServiceDirect: IServiceDirect {}

public interface IGenericInterface<T>: IService{}
public abstract class AGenericClass<T>: IGenericInterface<T> {}
public class GenericClass: IGenericInterface<MyEntity> {}

我需要在运行时获取所有继承自IService的服务,但调用_serviceProvider.GetServices<IService>()仅能返回直接继承该接口的服务。

当前服务注册逻辑如下(已省略筛选类/接口的代码):

assembly.ExportedType.ToList().ForEach(x => {
  //services is of type IServiceCollection
  services.AddScoped(x.Parent.IsGenericType 
       ? x.Parent.GenericTypeDefinition()
             .MakeGenericType(x.Parent.GetGenericArguments())  
       : x.Parent, x.Class);
}

该注册方式可按直接接口或抽象父类注入服务,但为了通过_serviceProvider.GetServices<IService>()获取所有服务,我添加了额外的显式注册逻辑:

assembly.ExportedType.ToList().ForEach(x => {
  if(x.Parent.IsGenericType)
  {
     services.AddScoped(x.Parent.GenericTypeDefinition()
         .MakeGenericType(x.Parent.GetGenericArguments())
             .GetInterfaces().FirstOrDefault(x => x == typeof(IService)),  
       x.Class);
  }

  //services is of type IServiceCollection
  services.AddScoped(x.Parent.IsGenericType 
       ? x.Parent.GenericTypeDefinition().MakeGenericType(
             x.Parent.GetGenericArguments())  
       : x.Parent, x.Class);
}

目前已能正常获取所有服务,但想咨询:是否存在无需显式注册所需接口的更优实现方式?


解决方案

当然有更简洁的实现方式,核心思路是直接识别所有实现了IService的具体类(无论继承层级),并批量注册为IService,同时保留原有按父接口/抽象类注册的逻辑,无需单独针对泛型场景写特殊处理:

  1. 批量扫描并注册所有IService实现类
    先从程序集中筛选出所有非抽象、非接口且**间接或直接实现IService**的类型,然后统一注册为IService:
var serviceTypes = assembly.ExportedTypes
    .Where(type => !type.IsAbstract && !type.IsInterface 
                && typeof(IService).IsAssignableFrom(type));

foreach (var type in serviceTypes)
{
    // 注册为IService,确保GetServices<IService>()能拿到所有实例
    services.AddScoped(typeof(IService), type);
    
    // 保留原有按父接口/抽象类注册的逻辑
    var parentType = type.BaseType != null && type.BaseType.IsGenericType 
        ? type.BaseType.GetGenericTypeDefinition().MakeGenericType(type.BaseType.GetGenericArguments())
        : type.GetInterfaces().FirstOrDefault(); // 可根据实际需求调整筛选规则
    
    if (parentType != null)
    {
        services.AddScoped(parentType, type);
    }
}
  1. 封装为扩展方法简化调用
    可以把注册逻辑封装成扩展方法,让代码更整洁易维护:
public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddAllServices(this IServiceCollection services, Assembly assembly)
    {
        var serviceTypes = assembly.ExportedTypes
            .Where(t => !t.IsAbstract && !t.IsInterface && typeof(IService).IsAssignableFrom(t));

        foreach (var type in serviceTypes)
        {
            // 注册为IService
            services.AddScoped(typeof(IService), type);
            
            // 获取需要注册的父类型(抽象类/直接接口)
            var parentToRegister = GetParentTypeToRegister(type);
            if (parentToRegister != null)
            {
                services.AddScoped(parentToRegister, type);
            }
        }
        
        return services;
    }

    private static Type? GetParentTypeToRegister(Type type)
    {
        // 优先取直接继承的抽象类(含泛型)
        if (type.BaseType != null && !type.BaseType.IsInterface && !type.BaseType.IsSealed)
        {
            return type.BaseType.IsGenericType 
                ? type.BaseType.GetGenericTypeDefinition().MakeGenericType(type.BaseType.GetGenericArguments())
                : type.BaseType;
        }
        
        // 无抽象父类则取直接实现的第一个接口
        return type.GetInterfaces().FirstOrDefault();
    }
}

使用时只需一行代码:

services.AddAllServices(assembly);

这种方式的优势:

  • typeof(IService).IsAssignableFrom(type)会自动识别所有间接实现IService的类,无需针对泛型写特殊判断;
  • 逻辑清晰,批量处理所有符合条件的服务类;
  • 保留原有按父接口/抽象类注册的能力,不影响其他注入场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 23:10:55