代码重构 - 泛型继承与安全检查 - 泛型递归约束 - Curiously Recurring Template Pattern (CRTP)

Unity事件委托时,要设计事件类(类似Qt的信号类),实现基本一样就类型类名不一样,需要抽象

[!tip] 重构技巧
两个类的实现代码完全一样, 就只有类名或类型不一样的时候, 而且还需要不断扩展 (未来会增加各种事件) 的时候, 这时候就用泛型 + 继承来提取

  • 继承解决扩展的问题,
  • 泛型解决实现代码一致, 类不一致的问题

C# 实现

image

image

using System;

namespace FrameworkDesign.Example
{
    // 泛型事件基类(带递归约束)
    // 约束:T 必须是继承自 EventBase<T> 的具体事件类型(自身类型约束)
    public abstract class EventBase<T> where T : EventBase<T>, new()
    {
        // 静态委托链:存储当前事件类型的所有订阅者
        private static Action mOnEvent;

        // 单例实例(确保每个具体事件类型只有一个实例)
        private static T mInstance;
        public static T Instance => mInstance ?? (mInstance = new T());

        // 注册订阅者
        public static void Register(Action onEvent)
        {
            if (onEvent != null)
                mOnEvent += onEvent;
        }

        // 注销订阅者
        public static void UnRegister(Action onEvent)
        {
            if (onEvent != null)
                mOnEvent -= onEvent;
        }

        // 触发事件(由具体事件类型调用)
        protected void TriggerEvent()
        {
            mOnEvent?.Invoke();
        }

        // 具体事件的触发逻辑(留给子类实现)
        public abstract void Trigger();
    }


    // --------------------------
    // 具体事件类型示例(继承通用框架)
    // --------------------------

    // 游戏开始事件(继承通用基类,满足递归约束)
    public class GameStartEvent : EventBase<GameStartEvent>
    {
        // 实现具体的触发逻辑
        public override void Trigger()
        {
            // 可以添加事件触发前的自定义逻辑
            Console.WriteLine("游戏开始事件触发前准备...");
            
            // 调用基类的触发方法
            TriggerEvent();
        }
    }

    // 游戏结束事件(另一个具体事件)
    public class GameOverEvent : EventBase<GameOverEvent>
    {
        public override void Trigger()
        {
            Console.WriteLine("游戏结束事件触发前准备...");
            TriggerEvent();
        }
    }
}

Cpp要实现只需要在顶层泛型调用时添加编译期检查即可,直接使用继承即可

// 转换为派生类(编译期检查:如果 Derived 不是正确的派生类,会报错)
static_cast<Derived*>(this)->PrintImpl();

posted @ 2025-11-07 14:46  丘狸尾  阅读(0)  评论(0)    收藏  举报