IoC 包使用说明
概述
IoC(Inversion of Control,控制反转)包提供了一个轻量级的依赖注入容器,用于管理框架中各种组件的注册和获取。通过 IoC 容器,可以实现组件间的解耦,便于测试和维护。
IoC 容器是 GFramework 架构的核心组件之一,为整个框架提供依赖管理和组件解析服务。
核心类
IocContainer
IoC 容器类,负责管理对象的注册和获取。
继承关系:
public class IocContainer : ContextAwareBase, IIocContainer主要功能:
- 注册实例到容器
- 从容器中获取实例
- 类型安全的依赖管理
- 线程安全操作
- 容器冻结保护
- 多实例注册支持
核心方法
Register<T> 和 Register(Type, object)
注册一个实例到容器中。
方法签名:
public void Register<T>(T instance)参数:
instance: 要注册的实例对象
特点:
- 支持泛型注册
- 自动注册到实例的所有接口类型
- 线程安全操作
- 容器冻结后抛出异常
使用示例:
var container = new IocContainer();
// 注册各种类型的实例
container.Register<IPlayerModel>(new PlayerModel());
container.Register<IGameSystem>(new GameSystem());
container.Register<IStorageUtility>(new StorageUtility());注意: 非泛型的 Register(Type, object) 方法是内部方法 RegisterInternal,不作为公开 API 使用。
RegisterSingleton<T>
注册单例实例到容器中。一个类型只允许一个实例。
方法签名:
public void RegisterSingleton<T>(T instance)参数:
instance: 要注册的单例实例
特点:
- 严格单例约束:同一类型只能注册一个实例
- 重复注册会抛出
InvalidOperationException - 适用于全局服务和配置对象
使用示例:
var container = new IocContainer();
// 注册单例
container.RegisterSingleton<IPlayerModel>(new PlayerModel());
container.RegisterSingleton<IGameConfiguration>(new GameConfiguration());
// 以下会抛出异常(重复注册)
// container.RegisterSingleton<IPlayerModel>(new AnotherPlayerModel());RegisterPlurality
注册多个实例,将实例注册到其实现的所有接口和具体类型上。
方法签名:
public void RegisterPlurality(object instance)参数:
instance: 要注册的实例
特点:
- 自动注册到所有实现的接口
- 同时注册具体类型
- 支持多态注册
- 常用于 System 和 Utility 注册
使用示例:
var container = new IocContainer();
// 假设 GameSystem 实现了 IGameSystem 和 IUpdateable 接口
var gameSystem = new GameSystem();
container.RegisterPlurality(gameSystem);
// 现在可以通过任一接口获取
var system1 = container.Get<IGameSystem>(); // 返回 gameSystem
var system2 = container.Get<IUpdateable>(); // 返回 gameSystem
var system3 = container.Get<GameSystem>(); // 返回 gameSystemRegisterSystem(Architecture 方法)
注意: RegisterSystem 是 Architecture 类的方法,不是 IocContainer 的方法。它用于在架构中注册系统实例。
方法签名(在 Architecture 中):
public TSystem RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem特点:
- 专门为 System 设计的注册方法
- 内部调用
IocContainer.RegisterPlurality - 自动处理 System 的生命周期
- 提供语义化的 API
使用示例:
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册系统
RegisterSystem(new CombatSystem());
RegisterSystem(new InventorySystem());
RegisterSystem(new QuestSystem());
}
}Get<T> 和 GetAll<T>
从容器中获取指定类型的实例。
方法签名:
public T? Get<T>() where T : class
public IReadOnlyList<T> GetAll<T>() where T : class返回值:
Get<T>: 返回指定类型的实例,如果未找到则返回nullGetAll<T>: 返回指定类型的所有实例列表,如果未找到则返回空数组
特点:
- 线程安全读取
- 支持接口和具体类型查询
- 返回值快照(线程安全)
使用示例:
// 获取已注册的实例
var playerModel = container.Get<IPlayerModel>();
var gameSystems = container.GetAll<IGameSystem>();
// 如果类型未注册,Get 返回 null,GetAll 返回空数组
var unknownService = container.Get<IUnknownService>(); // null
var allUnknownServices = container.GetAll<IUnknownService>(); // 空数组GetRequired<T>
获取指定类型的必需实例,如果没有注册或注册了多个实例会抛出异常。
方法签名:
public T GetRequired<T>() where T : class返回值:
- 返回找到的唯一实例
特点:
- 严格模式获取
- 无实例时抛出
InvalidOperationException - 多实例时抛出
InvalidOperationException - 适用于必需的单例依赖
使用示例:
// 获取必需的服务
var config = container.GetRequired<IGameConfiguration>(); // 必须存在且唯一
// 以下情况会抛出异常:
// 1. IGameConfiguration 未注册
// 2. IGameConfiguration 注册了多个实例GetAllSorted<T>
获取并排序(系统调度专用)。
方法签名:
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class参数:
comparison: 比较器委托,定义排序规则
返回值:
- 按指定方式排序后的实例列表
特点:
- 支持自定义排序
- 适用于需要按优先级执行的场景
- 常用于 System 更新顺序控制
使用示例:
// 按优先级排序系统
var sortedSystems = container.GetAllSorted<ISystem>((a, b) =>
{
var priorityA = GetSystemPriority(a);
var priorityB = GetSystemPriority(b);
return priorityA.CompareTo(priorityB);
});IoC 容器架构
Architecture (架构层)
├── IocContainer (IoC容器)
│ ├── Register<T>() // 泛型注册
│ ├── Register(Type, obj) // 非泛型注册
│ ├── RegisterSingleton<T>() // 单例注册
│ ├── RegisterPlurality() // 多态注册
│ ├── RegisterSystem() // 系统注册
│ ├── Get<T>() // 获取实例
│ ├── GetAll<T>() // 获取所有实例
│ ├── GetRequired<T>() // 获取必需实例
│ ├── GetAllSorted<T>() // 排序获取
│ ├── Contains<T>() // 检查存在性
│ ├── ContainsInstance() // 检查实例
│ ├── Clear() // 清空容器
│ └── Freeze() // 冻结容器
│
├── Components (组件)
│ ├── Model (数据模型)
│ ├── System (业务系统)
│ └── Utility (工具类)
│
└── Context (上下文)
└── 提供容器访问在框架中的使用
Architecture 中的应用
IoC 容器是 Architecture 类的核心组件,用于管理所有的 System、Model 和 Utility。
public abstract class Architecture : IArchitecture
{
// 内置 IoC 容器
private readonly IocContainer _mContainer = new();
// 注册系统
public TSystem RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
{
system.SetContext(Context);
_mContainer.RegisterPlurality(system); // 注册到容器
RegisterLifecycleComponent(system); // 处理生命周期
return system;
}
// 获取系统
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
=> _mContainer.Get<TSystem>(); // 从容器获取
// Model 和 Utility 同理
public TModel RegisterModel<TModel>(TModel model) where TModel : IModel
{
model.SetContext(Context);
_mContainer.RegisterPlurality(model);
RegisterLifecycleComponent(model);
return model;
}
public TModel GetModel<TModel>() where TModel : class, IModel
=> _mContainer.Get<TModel>();
}注册组件到容器
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 这些方法内部都使用 IoC 容器
// 注册 Model(存储游戏数据)
RegisterModel<IPlayerModel>(new PlayerModel());
RegisterModel<IInventoryModel>(new InventoryModel());
// 注册 System(业务逻辑)
RegisterSystem<IGameplaySystem>(new GameplaySystem());
RegisterSystem<ISaveSystem>(new SaveSystem());
// 注册 Utility(工具类)
RegisterUtility<ITimeUtility>(new TimeUtility());
RegisterUtility<IStorageUtility>(new StorageUtility());
}
}从容器获取组件
// 通过扩展方法间接使用 IoC 容器
public class PlayerController : IController
{
public void Start()
{
// GetModel 内部调用 Architecture.GetModel
// Architecture.GetModel 内部调用 IocContainer.Get
var playerModel = this.GetModel<IPlayerModel>();
var gameplaySystem = this.GetSystem<IGameplaySystem>();
var timeUtility = this.GetUtility<ITimeUtility>();
}
}工作原理
内部实现
public class IocContainer
{
// 使用字典存储类型到实例集合的映射
private readonly Dictionary<Type, HashSet<object>> _typeIndex = new();
private readonly HashSet<object> _objects = [];
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
private volatile bool _frozen = false;
public void Register<T>(T instance)
{
// 获取写锁以确保线程安全
_lock.EnterWriteLock();
try
{
RegisterInternal(typeof(T), instance!);
}
finally
{
_lock.ExitWriteLock();
}
}
public T? Get<T>() where T : class
{
_lock.EnterReadLock();
try
{
if (_typeIndex.TryGetValue(typeof(T), out var set) && set.Count > 0)
{
var result = set.First() as T;
return result;
}
return null;
}
finally
{
_lock.ExitReadLock();
}
}
private void RegisterInternal(Type type, object instance)
{
if (_frozen) throw new InvalidOperationException("IocContainer is frozen");
_objects.Add(instance);
if (!_typeIndex.TryGetValue(type, out var set))
{
set = [];
_typeIndex[type] = set;
}
set.Add(instance);
}
}线程安全机制
容器使用 ReaderWriterLockSlim 来确保线程安全操作,允许多个线程同时读取,但在写入时阻止其他线程访问。
注册流程
用户代码
↓
RegisterSystem<T>(system)
↓
IocContainer.Register<T>(system)
↓
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet获取流程
用户代码
↓
this.GetSystem<T>()
↓
Architecture.GetSystem<T>()
↓
IocContainer.Get<T>()
↓
加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet
↓
返回HashSet中第一个实例或 null使用示例
基础使用
// 1. 创建容器
var container = new IocContainer();
// 2. 注册服务
var playerService = new PlayerService();
container.Register<IPlayerService>(playerService);
// 3. 获取服务
var service = container.Get<IPlayerService>();
service.DoSomething();接口和实现分离
// 定义接口
public interface IDataService
{
void SaveData(string data);
string LoadData();
}
// 实现类
public class LocalDataService : IDataService
{
public void SaveData(string data) { /* 本地存储 */ }
public string LoadData() { /* 本地加载 */ return ""; }
}
public class CloudDataService : IDataService
{
public void SaveData(string data) { /* 云端存储 */ }
public string LoadData() { /* 云端加载 */ return ""; }
}
// 注册(可以根据配置选择不同实现)
var container = new IocContainer();
#if CLOUD_SAVE
container.Register<IDataService>(new CloudDataService());
#else
container.Register<IDataService>(new LocalDataService());
#endif
// 使用(不需要关心具体实现)
var dataService = container.Get<IDataService>();
dataService.SaveData("game data");注册多个实现
var container = new IocContainer();
// 注册多个相同接口的不同实现
container.Register<IDataService>(new LocalDataService());
container.Register<IDataService>(new CloudDataService());
// 获取单个实例(返回第一个)
var singleService = container.Get<IDataService>(); // 返回第一个注册的实例
// 获取所有实例
var allServices = container.GetAll<IDataService>(); // 返回两个实例的列表其他实用方法
Contains<T>()
检查容器中是否包含指定类型的实例。
public bool Contains<T>() where T : class参数:
- 无泛型参数
返回值:
- 如果容器中包含指定类型的实例则返回
true,否则返回false
使用示例:
var container = new IocContainer();
// 检查服务是否已注册
if (container.Contains<IPlayerService>())
{
Console.WriteLine("Player service is available");
}
// 根据检查结果决定是否注册
if (!container.Contains<ISettingsService>())
{
container.Register<ISettingsService>(new SettingsService());
}应用场景:
- 条件注册服务
- 检查依赖是否可用
- 动态功能开关
ContainsInstance(object instance)
判断容器中是否包含某个具体的实例对象。
public bool ContainsInstance(object instance)参数:
instance:待查询的实例对象
返回值:
- 若容器中包含该实例则返回
true,否则返回false
使用示例:
var container = new IocContainer();
var service = new MyService();
container.Register<IMyService>(service);
// 检查特定实例是否在容器中
if (container.ContainsInstance(service))
{
Console.WriteLine("This instance is registered in the container");
}
// 检查另一个实例
var anotherService = new MyService();
if (!container.ContainsInstance(anotherService))
{
Console.WriteLine("This instance is not in the container");
}应用场景:
- 避免重复注册同一实例
- 检查对象是否已被管理
- 调试和日志记录
Clear()
清空容器中的所有实例。
public void Clear()使用示例:
var container = new IocContainer();
// 注册多个服务
container.Register<IService1>(new Service1());
container.Register<IService2>(new Service2());
container.Register<IService3>(new Service3());
// 清空容器
container.Clear();
// 检查是否清空成功
Console.WriteLine($"Contains IService1: {container.Contains<IService1>()}"); // False
Console.WriteLine($"Contains IService2: {container.Contains<IService2>()}"); // False应用场景:
- 重置容器状态
- 内存清理
- 测试环境准备
注意事项:
- 容器冻结后也可以调用
Clear()方法 - 清空后,所有已注册的实例都将丢失
- 不会自动清理已注册对象的其他引用
设计特点
1. 简单轻量
- 支持多种注册方式:普通注册、单例注册、多实例注册
- 基于字典和哈希集实现,性能高效
- 无复杂的依赖解析逻辑
2. 手动注册
- 需要显式注册每个组件
- 不支持自动依赖注入
- 完全可控的组件生命周期
3. 多实例支持
- 每个类型可以注册多个实例
- 提供
GetAll方法获取所有实例 - 提供
Get方法获取单个实例
4. 类型安全
- 基于泛型,编译时类型检查
- 避免字符串键导致的错误
- IDE 友好,支持自动补全
5. 线程安全
- 使用读写锁确保多线程环境下的安全操作
- 读操作可以并发执行
- 写操作独占锁,防止并发修改冲突
6. 容器冻结
- 提供
Freeze方法,防止进一步修改容器内容 - 防止在初始化后意外修改注册内容
与其他 IoC 容器的区别
本框架的 IocContainer
// 简单直接
var container = new IocContainer();
container.Register(new MyService());
var service = container.Get<MyService>();特点:
- ✅ 简单易用
- ✅ 性能高
- ✅ 线程安全
- ✅ 支持多实例
- ❌ 不支持构造函数注入
- ❌ 不支持自动解析依赖
- ❌ 不支持生命周期管理(Transient/Scoped/Singleton)
完整的 IoC 框架(如 Autofac、Zenject)
// 复杂但功能强大
var builder = new ContainerBuilder();
builder.RegisterType<MyService>().As<IMyService>().SingleInstance();
builder.RegisterType<MyController>().WithParameter("config", config);
var container = builder.Build();
// 自动解析依赖
var controller = container.Resolve<MyController>();特点:
- ✅ 自动依赖注入
- ✅ 生命周期管理
- ✅ 复杂场景支持
- ❌ 学习成本高
- ❌ 性能开销大
最佳实践
1. 在架构初始化时注册
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 按顺序注册组件
// 1. 工具类(无依赖)
RegisterUtility(new TimeUtility());
RegisterUtility(new StorageUtility());
// 2. 模型(可能依赖工具)
RegisterModel(new PlayerModel());
RegisterModel(new GameModel());
// 3. 系统(可能依赖模型和工具)
RegisterSystem(new GameplaySystem());
RegisterSystem(new SaveSystem());
}
}2. 使用接口类型注册
// ❌ 不推荐:直接使用实现类
RegisterSystem(new ConcreteSystem());
var system = GetSystem<ConcreteSystem>();
// ✅ 推荐:使用接口
RegisterSystem<IGameSystem>(new ConcreteSystem());
var system = GetSystem<IGameSystem>();3. 避免运行时频繁注册
// ❌ 不好:游戏运行时频繁注册
void Update()
{
RegisterService(new TempService()); // 每帧创建
}
// ✅ 好:在初始化时一次性注册
protected override void Init()
{
RegisterService(new PersistentService());
}4. 检查 null 返回值
// 获取可能不存在的服务
var service = container.Get<IOptionalService>();
if (service != null)
{
service.DoSomething();
}
else
{
GD.Print("Service not registered!");
}5. 合理使用容器冻结
// 在架构初始化完成后冻结容器,防止意外修改
protected override void OnInit()
{
// 注册所有组件
RegisterModel(new PlayerModel());
RegisterSystem(new GameSystem());
// ...
// 冻结容器
Container.Freeze(); // 此后无法再注册新组件
}注意事项
线程安全操作:容器内部使用读写锁确保线程安全,无需额外同步
容器冻结:一旦调用
Freeze方法,将不能再注册新实例**单例注册限制 **:
RegisterSingleton方法确保一个类型只能有一个实例,重复注册会抛出异常内存管理:容器持有的实例不会自动释放,需要注意内存泄漏问题
注册顺序:组件的依赖关系需要手动保证,先注册被依赖的组件
相关包
architecture- 使用 IoC 容器管理所有组件model- Model 通过 IoC 容器注册和获取system- System 通过 IoC 容器注册和获取utility- Utility 通过 IoC 容器注册和获取