0Pricing
C# Academy · 课时

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 反馈 — 无需本地设置。

此课程中的所有课时

  1. DI 容器基础
  2. 服务生命周期:Transient、Scoped、Singleton
  3. 构造函数注入与接口
  4. 工厂与选项模式
← 返回 C# Academy