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

如何从泛型基类推断实现类型?.NET依赖注入场景

解决方案

要实现只传入仓库类型就能生成对应实体的Get<T>委托,有几种实用方案:

方案1:基于反射的扩展方法(无需修改现有代码)

直接写一个扩展方法,自动从仓库类型中提取对应的实体类型,完成委托注册:

public static class ServiceCollectionExtensions
{
    public static void AddGetDelegate<TRepo>(this IServiceCollection services)
        where TRepo : class
    {
        var repoType = typeof(TRepo);
        // 确认仓库继承自RepositoryBase<T>
        var baseType = repoType.BaseType;
        if (baseType == null || !baseType.IsGenericType || 
            baseType.GetGenericTypeDefinition() != typeof(RepositoryBase<>))
        {
            throw new ArgumentException($"{repoType.Name} 必须继承自 RepositoryBase<TEntity>");
        }

        var entityType = baseType.GetGenericArguments()[0];
        var getDelegateType = typeof(Get<>).MakeGenericType(entityType);

        // 注册委托:从容器获取仓库实例,绑定Get方法
        services.AddScoped(getDelegateType, sp =>
        {
            var repo = sp.GetRequiredService<TRepo>();
            var getMethod = repoType.GetMethod(nameof(RepositoryBase<IEntity>.Get)) 
                            ?? baseType.GetMethod(nameof(RepositoryBase<IEntity>.Get));
            return Delegate.CreateDelegate(getDelegateType, repo, getMethod);
        });
    }
}

使用时只需一行代码,完全符合你想要的简洁性:

services.AddGetDelegate<FooRepository>();

这个方法会自动推断FooRepository对应的实体是Foo,并注册Get<Foo>委托到容器中。

方案2:给仓库基类添加实体类型标识(类型更安全)

如果不想用反射,可以修改RepositoryBase<T>,暴露对应的实体类型,然后通过泛型约束绑定:

public abstract class RepositoryBase<TEntity> where TEntity : class, IEntity
{
    public abstract Task<TEntity> Get(Guid id);
    // 新增静态属性,标记当前仓库对应的实体类型
    public static Type EntityType => typeof(TEntity);
}

然后编写辅助方法:

public static Get<TEntity> From<TRepo>(IServiceProvider services)
    where TRepo : RepositoryBase<TEntity>, class
    where TEntity : class, IEntity
{
    var repo = services.GetRequiredService<TRepo>();
    return repo.Get;
}

配合扩展方法进一步简化注册:

public static void AddGetDelegate<TRepo, TEntity>(this IServiceCollection services)
    where TRepo : RepositoryBase<TEntity>
    where TEntity : class, IEntity
{
    services.AddScoped<Get<TEntity>>(sp => From<TRepo>(sp));
}

使用时:

services.AddGetDelegate<FooRepository, Foo>();

虽然还是需要两个类型参数,但写法更规整。

方案3:给每个仓库添加静态委托工厂(最类型安全,但代码量略增)

如果追求绝对的类型安全,不想用反射,可以给每个仓库类添加静态方法:

public class FooRepository : RepositoryBase<Foo>
{
    public static Get<Foo> GetFactory(IServiceProvider sp) 
        => sp.GetRequiredService<FooRepository>().Get;
}

使用时:

services.AddScoped<Get<Foo>>(FooRepository.GetFactory);

这种方式没有反射开销,但需要给每个仓库写对应的静态方法。

总结

最推荐方案1的扩展方法,它无需修改现有仓库类和基类,只需一行代码就能完成注册,完全满足你想要的简洁用法,同时兼顾灵活性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:05:24