如何实例化类时仅传入构造函数中非注入服务的参数?
几种可行的实现方案
方案1:服务定位器模式(简单直接,但要注意作用域)
可以搞个静态的服务容器持有者,在程序启动时把DI容器传进去,然后Game的构造函数里直接从这个持有者拿ISessionsRepository实例。
代码示例:
// 先整个静态类存服务容器 public static class ServiceLocator { public static IServiceProvider? Provider { get; set; } } // 修改Game类的构造函数 public class Game { private ISessionsRepository repo; public Game(string sessionID, string sessionName) { // 从服务定位器里拿依赖 repo = ServiceLocator.Provider?.GetRequiredService<ISessionsRepository>() ?? throw new InvalidOperationException("服务容器还没初始化呢"); // 你的业务逻辑 } }
然后在程序启动的时候(比如ASP.NET Core的Program.cs)初始化这个静态类:
var builder = WebApplication.CreateBuilder(args); // 先把ISessionsRepository的实现注册到DI里 builder.Services.AddScoped<ISessionsRepository, SessionsRepository>(); var app = builder.Build(); // 把DI容器传给ServiceLocator ServiceLocator.Provider = app.Services; app.Run();
要提醒下:如果ISessionsRepository是Scoped作用域的服务,直接从根容器拿可能会有生命周期问题——比如在请求外创建Game实例的话,拿到的Scoped实例不会自动释放。所以最好在正确的作用域里创建Game,或者把依赖改成Transient的。
方案2:静态注入(适合单例/瞬态依赖)
如果你的ISessionsRepository是单例或者瞬态的,可以提前把实例注入到Game的静态字段里,然后构造函数直接用这个静态实例。
代码示例:
public class Game { private static ISessionsRepository? _staticRepo; private ISessionsRepository repo; // 整个静态方法用来注入依赖 public static void SetRepository(ISessionsRepository repo) { _staticRepo = repo ?? throw new ArgumentNullException(nameof(repo)); } public Game(string sessionID, string sessionName) { repo = _staticRepo ?? throw new InvalidOperationException("Repository还没初始化"); // 你的业务逻辑 } }
启动的时候初始化:
var repo = app.Services.GetRequiredService<ISessionsRepository>(); Game.SetRepository(repo);
这种方式比服务定位器更简单,但只适合依赖是单例的场景,灵活性差点。
方案3:工厂类(更符合DI设计原则)
要是不想用静态或者服务定位器,可以整个Game工厂,工厂由DI管理,通过工厂来创建Game实例——虽然不是直接new Game(...),但可以封装得差不多。
代码示例:
// 先定义工厂接口 public interface IGameFactory { Game Create(string sessionID, string sessionName); } // 实现工厂,工厂自己从DI拿ISessionsRepository public class GameFactory : IGameFactory { private readonly ISessionsRepository _repo; public GameFactory(ISessionsRepository repo) { _repo = repo; } public Game Create(string sessionID, string sessionName) { return new Game(_repo, sessionID, sessionName); } } // 把Game的原构造函数改成internal,防止外部直接调用 public class Game { private ISessionsRepository repo; internal Game(ISessionsRepository injectedRepo, string sessionID, string sessionName) { repo = injectedRepo; // 你的业务逻辑 } }
然后把工厂注册到DI:
builder.Services.AddScoped<IGameFactory, GameFactory>();
用的时候先从DI拿工厂,再创建实例:
// 在需要创建Game的地方,先获取IGameFactory var gameFactory = serviceProvider.GetRequiredService<IGameFactory>(); Game game = gameFactory.Create("1", "name");
这种方式更符合依赖注入的设计思想,没有静态依赖,但需要通过工厂来创建。要是嫌麻烦,还能在Game类里整个静态的Create方法,内部调用工厂,这样调用起来就和直接new差不多了。
内容的提问来源于stack exchange,提问作者Erray1
相关产品推荐
相关产品推荐

