15. Built-in Tool: TypeEventSystem

In addition to providing a framework, QFramework also provides three tools that can be used independently of the framework: TypeEventSystem, EasyEvent, BindableProperty, and IOCContainer.

These tools were not intentionally provided, but were created by combining these three tools in the design of QFramework’s architecture.

In this article, we will learn how to use TypeEventSystem.

Basic Usage

using UnityEngine;

namespace QFramework.Example
{
    public class TypeEventSystemBasicExample : MonoBehaviour
    {
        public struct TestEventA
        {
            public int Age;
        }

        private void Start()
        {
            TypeEventSystem.Global.Register<TestEventA>(e =>
            {
                Debug.Log(e.Age);
            }).UnRegisterWhenGameObjectDestroyed(gameObject);
        }

        private void Update()
        {
            // 鼠标左键点击
            if (Input.GetMouseButtonDown(0))
            {
                TypeEventSystem.Global.Send(new TestEventA()
                {
                    Age = 18
                });
            }

            // 鼠标右键点击
            if (Input.GetMouseButtonDown(1))
            {
                TypeEventSystem.Global.Send<TestEventA>();
            }
        }
    }
}

// 输出结果// 点击鼠标左键,则输出:// 18// 点击鼠标右键,则输出:// 0

This is the most basic usage of TypeEventSystem.

Event Inheritance Support

In addition to basic usage, TypeEventSystem’s events also support inheritance.

The sample code is as follows:

using UnityEngine;

namespace QFramework.Example
{
    public class TypeEventSystemInheritEventExample : MonoBehaviour
    {
        public interface IEventA
        {

        }

        public struct EventA : IEventA
        {

        }

        public struct EventB : IEventA
        {

        }

        private void Start()
        {
            TypeEventSystem.Global.Register<IEventA>(e =>
            {
                if (e is EventA)
                {
                    Debug.Log("TypeEvent Inherit EventA " + e.GetAge());
                }
                if (e is EventB)
                {
                    Debug.Log("TypeEvent Inherit EventB " + e.GetAge());
                }
              Debug.Log(e.GetType().Name);
            }).UnRegisterWhenGameObjectDestroyed(gameObject);
        }

        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                TypeEventSystem.Global.Send<IEventA>(new EventB());
                // 无效
                TypeEventSystem.Global.Send<EventB>();
            }
        }
    }
}


// 输出结果:// 当按下鼠标左键时,输出:// EventB

The code is not difficult.

Manual Unregistration of TypeEventSystem

If you want to control the unregistration of TypeEventSystem instead of automatic unregistration, it is also very simple. The code is as follows:

using UnityEngine;

namespace QFramework.Example
{
    public class TypeEventSystemUnRegisterExample : MonoBehaviour
    {

        public struct EventA
        {

        }

        private void Start()
        {
            TypeEventSystem.Global.Register<EventA>(OnEventA);
        }

        void OnEventA(EventA e)
        {

        }

        private void OnDestroy()
        {
            TypeEventSystem.Global.UnRegister<EventA>(OnEventA);
        }
    }
}

The code is also very simple.

Interface Event

TypeEventSystem also supports interface event mode. The sample code is as follows:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace QFramework.Example
{
    public struct InterfaceEventA
    {

    }

    public struct InterfaceEventB
    {

    }

    public class InterfaceEventModeExample : MonoBehaviour
        , IOnEvent<InterfaceEventA>
        , IOnEvent<InterfaceEventB>
    {
        public void OnEvent(InterfaceEventA e)
        {
            Debug.Log(e.GetType().Name);
        }

        public void OnEvent(InterfaceEventB e)
        {
            Debug.Log(e.GetType().Name);
        }

        private void Start()
        {
            this.RegisterEvent<InterfaceEventA>()
                .UnRegisterWhenGameObjectDestroyed(gameObject);

            this.RegisterEvent<InterfaceEventB>();
        }

        private void OnDestroy()
        {
            this.UnRegisterEvent<InterfaceEventB>();
        }

        private void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                TypeEventSystem.Global.Send<InterfaceEventA>();
                TypeEventSystem.Global.Send<InterfaceEventB>();
            }
        }
    }
}

// 输出结果
// 当按下鼠标左键时,输出:
// InterfaceEventA// InterfaceEventB

The code is very simple.

Similarly, interface events also support inheritance between events.

Interface events have better constraints, and you can write less code by completing the implementation of the interface through the code generation of the IDE, which is inspired by CorgiEngine and TopDownEngine.

How to Automatically Destroy Non-MonoBehavior Scripts

public class NoneMonoScript : IUnRegisterList
{
    public List<IUnRegister> UnregisterList { get; } = new List<IUnRegister>();


    void Start()
    {
        TypeEventSystem.Global.Register<EasyEventExample.EventA>(a =>
        {

        }).AddToUnregisterList(this);
    }

    void OnDestroy()
    {
        this.UnRegisterAll();
    }
}

Summary

If you want to manually unregister, you must create a method to receive events.

For automatic unregistration, use delegates directly.

Both have their own advantages and disadvantages, use them as needed.

In addition, it is best to use struct for event definition, because struct has less gc and can achieve better performance.

Interface events have better constraints and can also improve development efficiency through IDE code generation.

In summary, TypeEventSystem is a very powerful event tool.

Learning Summary

Interface Usage Example, Why Can RegisterEvent Be Accessed Using “this” (C# Feature)?

In the Start() method of the TypeEventSystemInterfaceExample class, RegisterEvent method can be accessed using the this keyword because the RegisterEvent method is defined as an extension method in the static class OnGlobalEventExtension, and the first parameter type of this extension method is IOnEvent<T>.

In C#, extension methods are a special type of static method that can be defined in a static class and used like instance methods. When an instance of a type calls an extension method, the compiler automatically passes that instance as the first parameter, so that it can be used in the method body.

In this case, the first parameter type of the RegisterEvent method is IOnEvent<T>, and the TypeEventSystemInterfaceExample class implements the IOnEvent<InterfaceEventA> and IOnEvent<InterfaceEventB> interfaces, so it is an instance of these two interface types. Therefore, in the Start() method, the RegisterEvent method can be called using this.RegisterEvent<InterfaceEventA>() and this.RegisterEvent<InterfaceEventB>(), where the this keyword represents the current instance of the TypeEventSystemInterfaceExample class.

By using extension methods, we can use the RegisterEvent method in the instance of the TypeEventSystemInterfaceExample class, making the code more readable and concise.