Files
FlowScope/docs/requirements/p0-container.md
2026-05-15 15:38:40 +08:00

130 lines
4.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P0-1: Container 需求详细文档
## 对齐说明
本文档以 `docs/requirements/p0-requirements-set.md` 为准。P0 允许 DI 自动注入进入首版,但必须保持 Container 核心轻量核心只负责注册、解析、作用域和释放Source Generator 与运行时反射只是生成工厂函数的适配层。
## 目标
提供 Game Core 的基础依赖容器支持显式工厂注册、Attribute + Source Generator 自动注入、Attribute + 运行时反射降级、子作用域和可预测释放。
## P0 功能需求
### 1. 显式注册
```csharp
public sealed class Container : IDisposable
{
public void RegisterInstance<T>(T instance);
public void RegisterFactory<T>(Func<Container, T> factory);
public T Resolve<T>();
public bool TryResolve<T>(out T value);
public Container CreateScope();
public void Dispose();
}
```
规则:
- `RegisterInstance<T>` 注册外部实例Container 不负责释放。
- `RegisterFactory<T>` 首次 Resolve 时创建实例,后续返回同一实例。
- 工厂创建且实现 `IDisposable` 的实例由当前 scope 释放。
- 子 scope 可访问父 scope 注册。
- 子 scope 可覆盖父 scope 注册,且不污染父 scope。
- 已 Dispose 的 scope 调用 Register/Resolve 必须抛 `ObjectDisposedException`
### 2. Attribute 自动注入
```csharp
[AttributeUsage(AttributeTargets.Class)]
public sealed class InjectableAttribute : Attribute
{
}
```
```csharp
public sealed class Container : IDisposable
{
public void RegisterType<TInterface, TImplementation>()
where TImplementation : TInterface;
public void RegisterType<TImplementation>();
public void RegisterAssembly();
}
```
规则:
- 自动注入只支持构造函数注入。
- 只允许一个 public 构造函数。
- 构造函数参数从当前 Container 递归 Resolve。
- 循环依赖必须检测,并输出完整依赖链。
- `[Inject]` 属性注入和方法注入不进入 P0。
### 3. Source Generator 推荐路径
Source Generator 是 P0 推荐自动注入路径。
要求:
- 编译期扫描 `[Injectable]` 类型。
- 为每个 Injectable 类型生成等价于手写工厂的静态工厂方法。
- 编译期检查构造函数依赖是否可解析,无法确认时发出诊断。
- Generator 程序集与 Runtime 程序集分离。
- 未启用 Source Generator 时,显式工厂模式仍可独立使用。
### 4. 运行时反射降级路径
运行时反射是 P0 可选降级路径。
要求:
- 仅在启用反射模式时使用。
- 反射只在首次 Resolve 时构造并缓存结果。
- 行为必须与 Source Generator 模式一致。
- IL2CPP/AOT 风险需要在文档中明确提示。
### 5. 释放规则
- 当前 scope Dispose 时按创建逆序释放本 scope 管理的 `IDisposable`
- 父 scope Dispose 时,先释放未释放的子 scope。
- `Dispose()` 可重复调用,后续调用静默忽略。
- 某个实例 Dispose 抛异常时,记录错误并继续释放其余实例。
## 暂不做
- `[Inject]` 属性注入。
- `[Inject]` 方法注入。
- Transient / Scoped / Singleton 多生命周期模式。
- 开放泛型注册。
- 装饰器注册。
- 条件注册。
- 运行时动态切换 DI 模式。
## 验收标准
| # | 标准 | 通过条件 |
|---|------|---------|
| 1 | 显式实例注册 | `RegisterInstance``Resolve` 返回同一实例 |
| 2 | 显式工厂注册 | `RegisterFactory` 首次 Resolve 创建实例,后续复用 |
| 3 | Source Generator 自动注入 | `[Injectable]` 类型可通过 `RegisterType` 正常解析 |
| 4 | 运行时反射自动注入 | 反射模式下行为与 Source Generator 一致 |
| 5 | 递归解析 | A 依赖 B、B 依赖 C 时 Resolve A 可自动解析整条链 |
| 6 | 循环依赖检测 | A -> B -> A 抛异常,错误包含依赖链 |
| 7 | 子 scope 读取父级 | 子 scope 可 Resolve 父 scope 注册 |
| 8 | 子 scope 覆盖父级 | 子 scope 覆盖后父 scope 不受影响 |
| 9 | 逆序释放 | 多个工厂实例按创建逆序 Dispose |
| 10 | 外部实例不释放 | `RegisterInstance` 传入的实例不由 Container Dispose |
| 11 | 重复 Dispose | 第二次 Dispose 不重复释放、不抛异常 |
| 12 | 已释放 scope 防护 | 已 Dispose scope 调用 Register/Resolve 抛 `ObjectDisposedException` |
## 依赖关系
```text
Container Runtime
├── 纯 C#,不依赖 Unity API
├── 不依赖 R3
├── Source Generator 适配层依赖 Roslyn
└── Reflection 适配层依赖 System.Reflection
```