依赖注入是C#项目中降低组件耦合度的常用设计模式,其核心思想是对象的依赖由外部容器提供,而非自身创建。要实现一个简单的依赖注入容器,核心逻辑可以拆分为服务注册和服务解析两部分,而反射和字典正是实现这两个功能的关键工具。

核心设计思路
依赖注入容器的基本工作流程是:先通过字典存储服务类型到实现类型的映射关系,也就是服务注册;当需要获取服务实例时,通过反射根据映射关系动态创建实现类型的对象,也就是服务解析。整个流程不需要手动new对象,完全由容器托管。
服务注册逻辑
服务注册的本质是把服务接口(或基类)和对应的实现类建立映射,存储到字典中。我们定义一个字典,键是服务类型,值是服务的生命周期和对应的实现类型,这里先实现最简单的瞬态生命周期,每次解析都创建新实例。
using System;
using System.Collections.Generic;
namespace SimpleDIContainer
{
// 容器类
public class DIContainer
{
// 存储服务类型到实现类型的映射字典
private readonly Dictionary<Type, Type> _serviceMappings = new Dictionary<Type, Type>();
/// <summary>
/// 注册服务,键为服务类型,值为实现类型
/// </summary>
/// <typeparam name="TService">服务类型(接口或基类)</typeparam>
/// <typeparam name="TImplementation">实现类型</typeparam>
public void Register<TService, TImplementation>() where TImplementation : TService
{
var serviceType = typeof(TService);
var implementationType = typeof(TImplementation);
// 检查是否已经注册过该服务
if (_serviceMappings.ContainsKey(serviceType))
{
throw new InvalidOperationException($"服务 {serviceType.Name} 已经被注册过了");
}
_serviceMappings.Add(serviceType, implementationType);
}
}
}
服务解析逻辑
服务解析时,首先从字典中获取服务对应的实现类型,然后通过反射创建该类型的实例。如果实现类型有构造函数,反射会自动匹配最合适的构造函数,这里我们先处理无参构造函数的情况,后续可以扩展带参构造函数的解析。
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SimpleDIContainer
{
public class DIContainer
{
private readonly Dictionary<Type, Type> _serviceMappings = new Dictionary<Type, Type>();
public void Register<TService, TImplementation>() where TImplementation : TService
{
var serviceType = typeof(TService);
var implementationType = typeof(TImplementation);
if (_serviceMappings.ContainsKey(serviceType))
{
throw new InvalidOperationException($"服务 {serviceType.Name} 已经被注册过了");
}
_serviceMappings.Add(serviceType, implementationType);
}
/// <summary>
/// 解析服务实例
/// </summary>
/// <typeparam name="TService">要解析的服务类型</typeparam>
/// <returns>服务实例</returns>
public TService Resolve<TService>()
{
return (TService)Resolve(typeof(TService));
}
/// <summary>
/// 内部解析方法,处理Type类型参数
/// </summary>
private object Resolve(Type serviceType)
{
// 检查服务是否已经注册
if (!_serviceMappings.TryGetValue(serviceType, out var implementationType))
{
throw new InvalidOperationException($"服务 {serviceType.Name} 未被注册");
}
// 获取实现类型的无参构造函数
var constructor = implementationType.GetConstructor(Type.EmptyTypes);
if (constructor == null)
{
throw new InvalidOperationException($"实现类型 {implementationType.Name} 没有无参构造函数");
}
// 通过反射调用构造函数创建实例
return constructor.Invoke(null);
}
}
}
实际使用示例
我们可以定义简单的服务接口和实现类,测试容器的功能是否正常。首先定义两个服务,一个是日志接口和实现,一个是用户服务,用户服务依赖日志服务。
using System;
namespace SimpleDIContainer
{
// 日志服务接口
public interface ILogService
{
void Log(string message);
}
// 日志服务实现
public class ConsoleLogService : ILogService
{
public void Log(string message)
{
Console.WriteLine($"日志内容:{message}");
}
}
// 用户服务接口
public interface IUserService
{
void AddUser(string userName);
}
// 用户服务实现,依赖ILogService
public class UserService : IUserService
{
private readonly ILogService _logService;
// 带参构造函数,后续可以扩展容器支持解析构造函数参数
public UserService(ILogService logService)
{
_logService = logService;
}
public void AddUser(string userName)
{
_logService.Log($"新增用户:{userName}");
}
}
}
然后编写测试代码,注册服务并解析使用:
using System;
namespace SimpleDIContainer
{
class Program
{
static void Main(string[] args)
{
var container = new DIContainer();
// 注册服务
container.Register<ILogService, ConsoleLogService>();
// 这里暂时只测试无参构造函数的服务,带参的需要扩展容器逻辑
// container.Register<IUserService, UserService>();
// 解析服务
var logService = container.Resolve<ILogService>();
logService.Log("依赖注入容器测试成功");
}
}
}
功能扩展方向
当前实现的容器只支持无参构造函数的瞬态服务,实际使用中还可以做很多扩展:比如支持带参构造函数的解析,自动递归解析构造函数的参数依赖;增加单例生命周期,同一个服务多次解析返回同一个实例;支持开放泛型注册,比如IRepository<T>映射到Repository<T>等。反射和字典的核心逻辑不变,只需要在现有基础上补充对应的判断和处理逻辑即可。
注意事项
- 反射创建对象的性能比直接new要低,如果对性能要求高的场景,可以加入表达式树缓存构造函数委托,减少反射调用次数。
- 注册服务时要确保实现类型可以转换为服务类型,泛型约束已经做了基础校验,非泛型注册时也需要做类型兼容性判断。
- 循环依赖的问题需要处理,比如服务A依赖服务B,服务B又依赖服务A,解析时会无限递归,需要增加循环依赖检测逻辑。
C#依赖注入反射DictionaryIOC修改时间:2026-07-02 05:24:42