如何从泛型基类推断实现类型?.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
相关产品推荐
相关产品推荐

