DI 容器基础
了解控制反转、服务注册,以及 .NET DI 容器如何解析依赖。
DI 容器基础 是 CoddyKit 上的免费 C# Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
什么是依赖注入?
依赖注入(DI)是一种设计模式,在这种模式中,类从外部来源接收其依赖项,而不是自行创建这些依赖项。
类不必在内部编写 var repo = new UserRepository();,而是声明所需的依赖项,由DI 容器提供这些依赖项。
控制反转
DI 是控制反转(IoC)原则的一种具体实现:创建依赖项的控制权从依赖类转移到了外部容器。
这样可以更轻松地测试和替换类。
设置 .NET DI 容器
在 .NET 应用中,DI 容器通过 IServiceCollection 在 Program.cs 中进行配置。服务会先注册,然后由运行时解析。
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<UserService>();
var app = builder.Build();
app.Run();定义接口与实现
DI 在面向接口编程时效果最佳。请为服务契约定义一个接口,并定义一个实现该接口的具体类。
public interface IUserRepository
{
User? GetById(int id);
}
public class UserRepository : IUserRepository
{
public User? GetById(int id)
{
// fetch from database
return new User { Id = id, Name = "Alice" };
}
}通过构造函数解析服务
容器会自动将已注册的服务注入构造函数。类只需声明它所需的接口即可——无需使用 new 关键字。
public class UserService
{
private readonly IUserRepository _repo;
public UserService(IUserRepository repo)
{
_repo = repo;
}
public User? FindUser(int id) => _repo.GetById(id);
}注册多个服务
您可以根据需要注册任意数量的服务。常用的扩展方法包括 AddTransient、AddScoped 和 AddSingleton——每种方法都会定义不同的生命周期。
builder.Services.AddSingleton<IConfig, AppConfig>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddTransient<IEmailService, SmtpEmailService>();
builder.Services.AddScoped<UserService>();直接使用 IServiceProvider
在少数情况下,您需要以命令式方式解析服务。IServiceProvider 允许您调用 GetRequiredService<T>() 来解析服务,但在大多数场景中应优先使用构造函数注入。
// Anti-pattern (service locator) — use sparingly
public class ManualResolver
{
private readonly IServiceProvider _sp;
public ManualResolver(IServiceProvider sp) => _sp = sp;
public void DoWork()
{
var svc = _sp.GetRequiredService<UserService>();
svc.FindUser(1);
}
}注册开放泛型
.NET DI 支持开放泛型注册。您可以注册一次 IRepository<>,它会针对任意实体类型解析为 Repository<T>。
builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
// Resolves as:
// IRepository<User> -> Repository<User>
// IRepository<Order> -> Repository<Order>启动时验证
通过 ValidateOnStart() 或 ValidateScopes = true,容器可以在应用程序启动时检测配置错误(例如在单例服务中捕获作用域服务),而不是等到运行时才发现。
builder.Services.AddScoped<IMyService, MyService>();
// Detect scope violations at startup
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true;
options.ValidateOnBuild = true;
});装饰服务
您可以使用装饰器包装服务,而无需修改原始类。请先注册内部服务,然后注册包装它的装饰器。
builder.Services.AddScoped<IUserRepository, UserRepository>();
// Decorator pattern
builder.Services.Decorate<IUserRepository, CachedUserRepository>();
// Requires Scrutor NuGet package实际应用:使用 DI 的最小 API
在最小 API 中,服务会直接注入路由处理程序参数。容器会自动解析这些服务。
app.MapGet("/users/{id}", (int id, UserService svc) =>
{
var user = svc.FindUser(id);
return user is null ? Results.NotFound() : Results.Ok(user);
});快速检查
在大多数类中,您应优先使用哪种方式获取依赖项?
回顾:DI 容器基础
要点总结:
- DI 反转了依赖项的创建过程:由容器构建对象,而不是由类自身构建
- 使用 AddTransient / AddScoped / AddSingleton 注册服务
- 构造函数注入是标准且推荐的方式
- 开放泛型、装饰器和 ValidateOnBuild 都是强大的高级功能
常见问题解答
「DI 容器基础」课时是免费的吗?
是的 — 「DI 容器基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「DI 容器基础」这节课中我会学到什么?
了解控制反转、服务注册,以及 .NET DI 容器如何解析依赖。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「DI 容器基础」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。