Events 包使用说明
概述
Events 包提供了一套完整的事件系统,实现了观察者模式(Observer Pattern)。通过事件系统,可以实现组件间的松耦合通信,支持无参和带参事件、事件注册/注销、以及灵活的事件组合。
事件系统是 GFramework 架构中组件间通信的核心机制,与命令模式和查询模式共同构成了完整的 CQRS 架构。
核心接口
IEvent
基础事件接口,定义了事件注册的基本功能。
核心方法:
csharp
IUnRegister Register(Action onEvent); // 注册事件处理函数IUnRegister
注销接口,用于取消事件注册。
核心方法:
csharp
void UnRegister(); // 执行注销操作IUnRegisterList
注销列表接口,用于批量管理注销对象。
属性:
csharp
IList<IUnRegister> UnregisterList { get; } // 获取注销列表IEventBus
事件总线接口,提供基于类型的事件发送和注册。
核心方法:
csharp
IUnRegister Register<T>(Action<T> onEvent); // 注册类型化事件
void Send<T>(T e); // 发送事件实例
void Send<T>() where T : new(); // 发送事件(自动创建实例)
void UnRegister<T>(Action<T> onEvent); // 注销事件监听器核心类
EasyEvent
无参事件类,支持注册、注销和触发无参事件。
核心方法:
csharp
IUnRegister Register(Action onEvent); // 注册事件监听器
void Trigger(); // 触发事件使用示例:
csharp
// 创建事件
var onClicked = new EasyEvent();
// 注册监听
var unregister = onClicked.Register(() =>
{
Console.WriteLine("Button clicked!");
});
// 触发事件
onClicked.Trigger();
// 取消注册
unregister.UnRegister();Event<T>
单参数泛型事件类,支持一个参数的事件。
核心方法:
csharp
IUnRegister Register(Action<T> onEvent); // 注册事件监听器
void Trigger(T eventData); // 触发事件并传递参数使用示例:
csharp
// 创建带参数的事件
var onScoreChanged = new Event<int>();
// 注册监听
onScoreChanged.Register(newScore =>
{
Console.WriteLine($"Score changed to: {newScore}");
});
// 触发事件并传递参数
onScoreChanged.Trigger(100);Event<T, TK>
双参数泛型事件类。
核心方法:
csharp
IUnRegister Register(Action<T, TK> onEvent); // 注册事件监听器
void Trigger(T param1, TK param2); // 触发事件并传递两个参数使用示例:
csharp
// 伤害事件:攻击者、伤害值
var onDamageDealt = new Event<string, int>();
onDamageDealt.Register((attacker, damage) =>
{
Console.WriteLine($"{attacker} dealt {damage} damage!");
});
onDamageDealt.Trigger("Player", 50);EasyEvents
全局事件管理器,提供类型安全的事件注册和获取。
核心方法:
csharp
static void Register<T>() where T : IEvent, new(); // 注册事件类型
static T Get<T>() where T : IEvent, new(); // 获取事件实例
static T GetOrAddEvent<T>() where T : IEvent, new(); // 获取或创建事件实例使用示例:
csharp
// 注册全局事件类型
EasyEvents.Register<GameStartEvent>();
// 获取事件实例
var gameStartEvent = EasyEvents.Get<GameStartEvent>();
// 注册监听
gameStartEvent.Register(() =>
{
Console.WriteLine("Game started!");
});
// 触发事件
gameStartEvent.Trigger();EventBus
类型化事件系统,支持基于类型的事件发送和注册。这是架构中默认的事件总线实现。
核心方法:
csharp
IUnRegister Register<T>(Action<T> onEvent); // 注册类型化事件
void Send<T>(T e); // 发送事件实例
void Send<T>() where T : new(); // 发送事件(自动创建实例)
void UnRegister<T>(Action<T> onEvent); // 注销事件监听器使用示例:
csharp
// 使用全局事件系统
var eventBus = new EventBus();
// 注册类型化事件
eventBus.Register<PlayerDiedEvent>(e =>
{
Console.WriteLine($"Player died at position: {e.Position}");
});
// 发送事件(传递实例)
eventBus.Send(new PlayerDiedEvent
{
Position = new Vector3(10, 0, 5)
});
// 发送事件(自动创建实例)
eventBus.Send<PlayerDiedEvent>();
// 注销事件监听器
eventBus.UnRegister<PlayerDiedEvent>(OnPlayerDied);DefaultUnRegister
默认注销器实现,封装注销回调。
使用示例:
csharp
Action onUnregister = () => Console.WriteLine("Unregistered");
var unregister = new DefaultUnRegister(onUnregister);
// 执行注销
unregister.UnRegister();OrEvent
事件或运算组合器,当任意一个事件触发时触发。
核心方法:
csharp
OrEvent Or(IEvent @event); // 添加要组合的事件使用示例:
csharp
var onAnyInput = new OrEvent()
.Or(onKeyPressed)
.Or(onMouseClicked)
.Or(onTouchDetected);
// 当上述任意事件触发时,执行回调
onAnyInput.Register(() =>
{
Console.WriteLine("Input detected!");
});UnRegisterList
批量管理注销对象的列表。
核心方法:
csharp
void Add(IUnRegister unRegister); // 添加注销器到列表
void UnRegisterAll(); // 批量注销所有事件使用示例:
csharp
var unregisterList = new UnRegisterList();
// 添加到列表
someEvent.Register(OnEvent).AddToUnregisterList(unregisterList);
// 批量注销
unregisterList.UnRegisterAll();ArchitectureEvents
定义了架构生命周期相关的事件。
包含事件:
ArchitectureLifecycleReadyEvent- 架构生命周期准备就绪ArchitectureDestroyingEvent- 架构销毁中ArchitectureDestroyedEvent- 架构已销毁ArchitectureFailedInitializationEvent- 架构初始化失败
在架构中使用事件
定义事件类
csharp
// 简单事件
public struct GameStartedEvent { }
// 带数据的事件
public struct PlayerDiedEvent
{
public Vector3 Position;
public string Cause;
}
// 复杂事件
public struct LevelCompletedEvent
{
public int LevelId;
public float CompletionTime;
public int Score;
public List<string> Achievements;
}Model 中发送事件
csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
protected override void OnInit()
{
// 监听生命值变化
Health.Register(newHealth =>
{
if (newHealth <= 0)
{
// 发送玩家死亡事件
this.SendEvent(new PlayerDiedEvent
{
Position = Position,
Cause = "Health depleted"
});
}
});
}
}System 中发送事件
csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit() { }
public void DealDamage(Character attacker, Character target, int damage)
{
target.Health -= damage;
// 发送伤害事件
this.SendEvent(new DamageDealtEvent
{
Attacker = attacker.Name,
Target = target.Name,
Damage = damage
});
}
}Controller 中注册事件
csharp
public class GameController : IController
{
private IUnRegisterList _unregisterList = new UnRegisterList();
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void Initialize()
{
// 注册多个事件
this.RegisterEvent<GameStartedEvent>(OnGameStarted)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted)
.AddToUnregisterList(_unregisterList);
}
private void OnGameStarted(GameStartedEvent e)
{
Console.WriteLine("Game started!");
}
private void OnPlayerDied(PlayerDiedEvent e)
{
Console.WriteLine($"Player died at {e.Position}: {e.Cause}");
ShowGameOverScreen();
}
private void OnLevelCompleted(LevelCompletedEvent e)
{
Console.WriteLine($"Level {e.LevelId} completed! Score: {e.Score}");
ShowVictoryScreen(e);
}
public void Cleanup()
{
_unregisterList.UnRegisterAll();
}
}高级用法
1. 事件链式组合
csharp
// 使用 Or 组合多个事件
var onAnyDamage = new OrEvent()
.Or(onPhysicalDamage)
.Or(onMagicDamage)
.Or(onPoisonDamage);
onAnyDamage.Register(() =>
{
PlayDamageSound();
});2. 事件过滤
csharp
// 只处理高伤害事件
this.RegisterEvent<DamageDealtEvent>(e =>
{
if (e.Damage >= 50)
{
ShowCriticalHitEffect();
}
});3. 事件转发
csharp
public class EventBridge : AbstractSystem
{
protected override void OnInit()
{
// 将内部事件转发为公共事件
this.RegisterEvent<InternalPlayerDiedEvent>(e =>
{
this.SendEvent(new PublicPlayerDiedEvent
{
PlayerId = e.Id,
Timestamp = DateTime.UtcNow
});
});
}
}4. 临时事件监听
csharp
public class TutorialController : IController
{
public IArchitecture GetArchitecture() => GameArchitecture.Interface;
public void Initialize()
{
// 只监听一次
IUnRegister unregister = null;
unregister = this.RegisterEvent<FirstEnemyKilledEvent>(e =>
{
ShowTutorialComplete();
unregister?.UnRegister(); // 立即注销
});
}
}5. 条件事件
csharp
public class AchievementSystem : AbstractSystem
{
private int _killCount = 0;
protected override void OnInit()
{
this.RegisterEvent<EnemyKilledEvent>(e =>
{
_killCount++;
// 条件满足时发送成就事件
if (_killCount >= 100)
{
this.SendEvent(new AchievementUnlockedEvent
{
AchievementId = "kill_100_enemies"
});
}
});
}
}生命周期管理
使用 UnRegisterList
csharp
public class MyController : IController
{
// 统一管理所有注销对象
private IUnRegisterList _unregisterList = new UnRegisterList();
public void Initialize()
{
// 所有注册都添加到列表
this.RegisterEvent<Event1>(OnEvent1)
.AddToUnregisterList(_unregisterList);
this.RegisterEvent<Event2>(OnEvent2)
.AddToUnregisterList(_unregisterList);
}
public void Cleanup()
{
// 一次性注销所有
_unregisterList.UnRegisterAll();
}
}最佳实践
事件命名规范
- 使用过去式:
PlayerDiedEvent、LevelCompletedEvent - 使用
Event后缀:便于识别 - 使用结构体:减少内存分配
- 使用过去式:
事件数据设计
- 只包含必要信息
- 使用值类型(struct)提高性能
- 避免传递可变引用
避免事件循环
- 事件处理器中谨慎发送新事件
- 使用命令打破循环依赖
合理使用事件
- 用于通知状态变化
- 用于跨模块通信
- 不用于返回数据(使用 Query)
注销管理
- 始终注销事件监听
- 使用
IUnRegisterList批量管理 - 在适当的生命周期点调用
Cleanup()
性能考虑
- 避免频繁触发的事件(如每帧)
- 事件处理器保持轻量
- 使用结构体事件减少 GC
事件设计原则
- 高内聚:事件应该代表一个完整的业务概念
- 低耦合:事件发送者不需要知道接收者
- 可测试:事件应该易于模拟和测试
事件 vs 其他通信方式
| 方式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Event | 状态变化通知、跨模块通信 | 松耦合、一对多 | 难以追踪调用链 |
| Command | 执行操作、修改状态 | 封装逻辑、可撤销 | 单向通信 |
| Query | 查询数据 | 职责清晰、有返回值 | 同步调用 |
| BindableProperty | UI 数据绑定 | 自动更新、响应式 | 仅限单一属性 |
事件系统架构
事件系统在 GFramework 中的架构位置:
Architecture (架构核心)
├── EventBus (事件总线)
├── CommandBus (命令总线)
├── QueryBus (查询总线)
└── IocContainer (IoC容器)
Components (组件)
├── Model (发送事件)
├── System (发送/接收事件)
└── Controller (接收事件)相关包
architecture- 提供全局事件系统extensions- 提供事件扩展方法property- 可绑定属性基于事件实现- Controller - 控制器监听事件(接口定义在 Core.Abstractions 中)
model- 模型发送事件system- 系统发送和监听事件command- 与事件配合实现 CQRSquery- 与事件配合实现 CQRS