---
title: IoC 包使用说明
description: 说明 GFramework.Core 的 IoC 容器、依赖注入职责与核心类型。
---

# IoC 包使用说明

## 概述

IoC（Inversion of Control，控制反转）包提供了一个轻量级的依赖注入容器，用于管理框架中各种组件的注册和获取。通过 IoC
容器，可以实现组件间的解耦，便于测试和维护。

IoC 容器是 GFramework 架构的核心组件之一，为整个框架提供依赖管理和组件解析服务。

## 核心类

### IocContainer

IoC 容器类，负责管理对象的注册和获取。

**继承关系：**

```csharp
public class IocContainer : ContextAwareBase, IIocContainer
```

**主要功能：**

- 注册实例到容器
- 从容器中获取实例
- 类型安全的依赖管理
- 线程安全操作
- 容器冻结保护
- 多实例注册支持

## 核心方法

### Register`<T>` 和 Register(Type, object)

注册一个实例到容器中。

**方法签名：**

```csharp
public void Register<T>(T instance)
```

**参数：**

- `instance`: 要注册的实例对象

**特点：**

- 支持泛型注册
- 自动注册到实例的所有接口类型
- 线程安全操作
- 容器冻结后抛出异常

**使用示例：**

```csharp
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>`

注册单例实例到容器中。一个类型只允许一个实例。

**方法签名：**

```csharp
public void RegisterSingleton<T>(T instance)
```

**参数：**

- `instance`: 要注册的单例实例

**特点：**

- 严格单例约束：同一类型只能注册一个实例
- 重复注册会抛出 `InvalidOperationException`
- 适用于全局服务和配置对象

**使用示例：**

```csharp
var container = new IocContainer();

// 注册单例
container.RegisterSingleton<IPlayerModel>(new PlayerModel());
container.RegisterSingleton<IGameConfiguration>(new GameConfiguration());

// 以下会抛出异常（重复注册）
// container.RegisterSingleton<IPlayerModel>(new AnotherPlayerModel());
```

### RegisterPlurality

注册多个实例，将实例注册到其实现的所有接口和具体类型上。

**方法签名：**

```csharp
public void RegisterPlurality(object instance)
```

**参数：**

- `instance`: 要注册的实例

**特点：**

- 自动注册到所有实现的接口
- 同时注册具体类型
- 支持多态注册
- 常用于 System 和 Utility 注册

**使用示例：**

```csharp
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>();    // 返回 gameSystem
```

### RegisterSystem（Architecture 方法）

**注意：** `RegisterSystem` 是 `Architecture` 类的方法，不是 `IocContainer` 的方法。它用于在架构中注册系统实例。

**方法签名（在 Architecture 中）：**

```csharp
public TSystem RegisterSystem<TSystem>(TSystem system) where TSystem : ISystem
```

**特点：**

- 专门为 System 设计的注册方法
- 内部调用 `IocContainer.RegisterPlurality`
- 自动处理 System 的生命周期
- 提供语义化的 API

**使用示例：**

```csharp
public class GameArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册系统
        RegisterSystem(new CombatSystem());
        RegisterSystem(new InventorySystem());
        RegisterSystem(new QuestSystem());
    }
}
```

### Get`<T>` 和 GetAll`<T>`

从容器中获取指定类型的实例。

**方法签名：**

```csharp
public T? Get<T>() where T : class
public IReadOnlyList<T> GetAll<T>() where T : class
```

**返回值：**

- `Get<T>`: 返回指定类型的实例，如果未找到则返回 `null`
- `GetAll<T>`: 返回指定类型的所有实例列表，如果未找到则返回空数组

**特点：**

- 线程安全读取
- 支持接口和具体类型查询
- 返回值快照（线程安全）

**使用示例：**

```csharp
// 获取已注册的实例
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>`

获取指定类型的必需实例，如果没有注册或注册了多个实例会抛出异常。

**方法签名：**

```csharp
public T GetRequired<T>() where T : class
```

**返回值：**

- 返回找到的唯一实例

**特点：**

- 严格模式获取
- 无实例时抛出 `InvalidOperationException`
- 多实例时抛出 `InvalidOperationException`
- 适用于必需的单例依赖

**使用示例：**

```csharp
// 获取必需的服务
var config = container.GetRequired<IGameConfiguration>();  // 必须存在且唯一

// 以下情况会抛出异常：
// 1. IGameConfiguration 未注册
// 2. IGameConfiguration 注册了多个实例
```

### GetAllSorted`<T>`

获取并排序（系统调度专用）。

**方法签名：**

```csharp
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class
```

**参数：**

- `comparison`: 比较器委托，定义排序规则

**返回值：**

- 按指定方式排序后的实例列表

**特点：**

- 支持自定义排序
- 适用于需要按优先级执行的场景
- 常用于 System 更新顺序控制

**使用示例：**

```csharp
// 按优先级排序系统
var sortedSystems = container.GetAllSorted<ISystem>((a, b) =>
{
    var priorityA = GetSystemPriority(a);
    var priorityB = GetSystemPriority(b);
    return priorityA.CompareTo(priorityB);
});
```

## IoC 容器架构

```text
Architecture (架构层)
├── IocContainer (IoC容器)
│   ├── Register<T>()          // 泛型注册
│   ├── Register(Type, obj)    // 非泛型注册
│   ├── RegisterSingleton<T>() // 单例注册
│   ├── RegisterPlurality()    // 多态注册
│   ├── RegisterSystem()       // 系统注册
│   ├── Get<T>()              // 获取实例
│   ├── GetAll<T>()           // 获取所有实例
│   ├── GetRequired<T>()      // 获取必需实例
│   ├── GetAllSorted<T>()     // 排序获取
│   ├── Contains<T>()         // 检查存在性
│   ├── HasRegistration()     // 检查服务键是否已注册
│   ├── ContainsInstance()    // 检查实例
│   ├── Clear()               // 清空容器
│   └── Freeze()              // 冻结容器
│
├── Components (组件)
│   ├── Model (数据模型)
│   ├── System (业务系统)
│   └── Utility (工具类)
│
└── Context (上下文)
    └── 提供容器访问
```

## 在框架中的使用

### Architecture 中的应用

IoC 容器是 [`Architecture`](./architecture.md) 类的核心组件，用于管理所有的 System、Model 和 Utility。

```csharp
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>();
}
```

### 注册组件到容器

```csharp
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());
    }
}
```

### 从容器获取组件

```csharp
// 通过扩展方法间接使用 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>();
    }
}
```

## 工作原理

### 内部实现

```csharp
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` 来确保线程安全操作，允许多个线程同时读取，但在写入时阻止其他线程访问。

### 注册流程

```text
用户代码
   ↓
RegisterSystem<T>(system)
   ↓
IocContainer.Register<T>(system)
   ↓
加写锁 -> Dictionary[typeof(T)] 添加实例到HashSet
```

### 获取流程

```text
用户代码
   ↓
this.GetSystem<T>()
   ↓
Architecture.GetSystem<T>()
   ↓
IocContainer.Get<T>()
   ↓
加读锁 -> Dictionary.TryGetValue(typeof(T)) 获取HashSet
   ↓
返回HashSet中第一个实例或 null
```

## 使用示例

### 基础使用

```csharp
// 1. 创建容器
var container = new IocContainer();

// 2. 注册服务
var playerService = new PlayerService();
container.Register<IPlayerService>(playerService);

// 3. 获取服务
var service = container.Get<IPlayerService>();
service.DoSomething();
```

### 接口和实现分离

```csharp
// 定义接口
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");
```

### 注册多个实现

```csharp
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>`()

检查容器中是否包含指定类型的实例。

```csharp
public bool Contains<T>() where T : class
```

**参数：**

- 无泛型参数

**返回值：**

- 如果容器中包含指定类型的实例则返回 `true`，否则返回 `false`

**使用示例：**

```csharp
var container = new IocContainer();

// 检查服务是否已注册
if (container.Contains<IPlayerService>())
{
    Console.WriteLine("Player service is available");
}

// 根据检查结果决定是否注册
if (!container.Contains<ISettingsService>())
{
    container.Register<ISettingsService>(new SettingsService());
}
```

**应用场景：**

- 条件注册服务
- 检查依赖是否可用
- 动态功能开关

### `HasRegistration(Type type)`

检查某个服务键或开放泛型映射是否已经注册，但不会为了判断结果先解析实例。

```csharp
public bool HasRegistration(Type type)
```

**参数：**

- `type`：要检查的服务类型

**返回值：**

- 若存在显式服务键注册，或开放泛型注册可以闭合到该服务类型，则返回 `true`
- 若只注册了实现类型自身、但没有把对应接口作为服务键注册，则返回 `false`

**特点：**

- 适合 request / pipeline 等热路径上的“先判断再解析”场景
- 不会激活瞬态实例，也不会触发多服务枚举
- 语义与 `Get(Type)` / `GetAll(Type)` 保持一致，按服务键而不是按“可赋值关系”判断可见性

**使用示例：**

```csharp
var behaviorServiceType = typeof(IPipelineBehavior<CreatePlayerRequest, PlayerCreated>);

if (container.HasRegistration(behaviorServiceType))
{
    foreach (var behavior in container.GetAll(behaviorServiceType))
    {
        Console.WriteLine(behavior.GetType().Name);
    }
}
```

### `ContainsInstance(object instance)`

判断容器中是否包含某个具体的实例对象。

```csharp
public bool ContainsInstance(object instance)
```

**参数：**

- `instance`：待查询的实例对象

**返回值：**

- 若容器中包含该实例则返回 `true`，否则返回 `false`

**使用示例：**

```csharp
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()`

清空容器中的所有实例。

```csharp
public void Clear()
```

**使用示例：**

```csharp
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

```csharp
// 简单直接
var container = new IocContainer();
container.Register(new MyService());
var service = container.Get<MyService>();
```

**特点：**

- ✅ 简单易用
- ✅ 性能高
- ✅ 线程安全
- ✅ 支持多实例
- ❌ 不支持构造函数注入
- ❌ 不支持自动解析依赖
- ❌ 不支持生命周期管理（Transient/Scoped/Singleton）

### 完整的 IoC 框架（如 Autofac、Zenject）

```csharp
// 复杂但功能强大
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. 在架构初始化时注册

```csharp
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. 使用接口类型注册

```csharp
// ❌ 不推荐：直接使用实现类
RegisterSystem(new ConcreteSystem());
var system = GetSystem<ConcreteSystem>();

// ✅ 推荐：使用接口
RegisterSystem<IGameSystem>(new ConcreteSystem());
var system = GetSystem<IGameSystem>();
```

### 3. 避免运行时频繁注册

```csharp
// ❌ 不好：游戏运行时频繁注册
void Update()
{
    RegisterService(new TempService());  // 每帧创建
}

// ✅ 好：在初始化时一次性注册
protected override void Init()
{
    RegisterService(new PersistentService());
}
```

### 4. 检查 null 返回值

```csharp
// 获取可能不存在的服务
var service = container.Get<IOptionalService>();
if (service != null)
{
    service.DoSomething();
}
else
{
    GD.Print("Service not registered!");
}
```

### 5. 合理使用容器冻结

```csharp
// 在架构初始化完成后冻结容器，防止意外修改
protected override void OnInit()
{
    // 注册所有组件
    RegisterModel(new PlayerModel());
    RegisterSystem(new GameSystem());
    // ...
    
    // 冻结容器
    Container.Freeze();  // 此后无法再注册新组件
}
```

## 注意事项

1. **线程安全操作**：容器内部使用读写锁确保线程安全，无需额外同步

2. **容器冻结**：一旦调用 `Freeze`
   方法，将不能再注册新实例

3. **单例注册限制
   **：`RegisterSingleton`
   方法确保一个类型只能有一个实例，重复注册会抛出异常

4. **内存管理**：容器持有的实例不会自动释放，需要注意内存泄漏问题

5. **注册顺序**：组件的依赖关系需要手动保证，先注册被依赖的组件

## 相关包

- [`architecture`](./architecture.md) - 使用 IoC 容器管理所有组件
- [`model`](./model.md) - Model 通过 IoC 容器注册和获取
- [`system`](./system.md) - System 通过 IoC 容器注册和获取
- [`utility`](./utility.md) - Utility 通过 IoC 容器注册和获取
