依赖注入的本质是解耦策略,即分离对象创建与使用:类不自行new依赖项,而是由容器通过构造函数注入等方式将依赖“送进来”,核心包括类型注册、实例解析和依赖递归。

手动实现一个简易 IOC 容器,核心是“用字典存类型映射 + 反射创建实例 + 递归解析构造函数依赖”,不依赖框架也能理清依赖注入本质。
依赖注入的本质是什么
依赖注入(DI)不是语法糖,而是一种解耦策略:把对象的创建和使用分离。类不再自己 new 依赖项,而是由外部(容器)把依赖“送进来”。最常见形式是构造函数注入——容器在创建类实例时,自动找到并传入它需要的所有参数类型实例。
关键点:
- 类型注册(告诉容器:接口 A 对应实现类 B)
- 实例解析(调用 GetService
- 依赖递归(若 T 的构造函数需要 U,U 需要 V……容器得一层层 resolve 出来)
手写简易 IOC 容器(支持构造函数注入)
以下是一个轻量、可运行的核心实现(不含生命周期管理、属性注入等高级特性,专注原理):
// 简单容器,仅支持瞬态(每次 Get 都新建)和单例注册
public class SimpleContainer
{
// 存注册信息:接口 → 实现类型 或 实例
private readonly Dictionary
private readonly Dictionary
// 注册接口→实现类(瞬态)
public void Register
{
_mappings[typeof(TInterface)] = typeof(TImplementation);
}
// 注册单例实例
public void RegisterSingleton
{
_singletons[typeof(TInterface)] = instance;
}
// 获取服务
public T GetService
private object Resolve(Type type)
{
// 优先查单例缓存
if (_singletons.TryGetValue(type, out var singleton))
return singleton;
// 查类型映射(如 IRepo → Repo)
if (_mappings.TryGetValue(type, out var implType))
type = implType;
// 检查是否已定义无参构造函数(简化版)
var ctor = type.GetConstructors().FirstOrDefault();
if (ctor == null)
throw new InvalidOperationException($"Type {type} has no accessible constructor.");
// 递归解析所有构造函数参数
var parameters = ctor.GetParameters();
var args = new object[parameters.Length];
for (int i = 0; i
{
args[i] = Resolve(parameters[i].ParameterType);
}
// 创建实例
return Activator.CreateInstance(type, args);
}
}
怎么用?举个实际例子
假设你有三层结构:
public interface ILogService { void Log(string msg); }
public class ConsoleLogService : ILogService { public void Log(string msg) => Console.WriteLine(msg); }
public interface IOrderRepository { void Save(Order o); }
public class OrderRepository : IOrderRepository
{
private readonly ILogService _log;
public OrderRepository(ILogService log) => _log = log;
public void Save(Order o) { _log.Log($"Saved {o.Id}"); }
}
public class OrderService
{
private readonly IOrderRepository _repo;
public OrderService(IOrderRepository repo) => _repo = repo;
}
注册与使用:
var container = new SimpleContainer();
container.Register
container.Register
var service = container.GetService
service.GetType().Name; // OrderService —— 已自动注入 repo → log
关键细节与注意事项
循环依赖会栈溢出:A 依赖 B,B 又依赖 A,Resolve 会无限递归。真实容器会加“正在创建中”标记检测。
泛型类型需特殊处理:如 Register,上面代码未支持,需用 Expression 或泛型注册表。
构造函数重载问题:当前只取第一个构造函数。生产级容器会选参数最多、且所有参数都能被解析的那个。
性能考虑:反射慢,可缓存 ConstructorInfo 和 ParameterInfo,甚至用 Emit 或 Source Generator 生成工厂方法。
生命周期不止 Transient/Singleton:Scoped(作用域内单例,如 Web 请求)需要上下文管理,上面没实现。
基本上就这些。手动写一遍,比直接用 Microsoft.Extensions.DependencyInjection 更容易看清“谁创建谁”“依赖怎么连起来”的脉络。不复杂但容易忽略递归解析和类型映射这两步。









