Csharp-事件

Csharp-事件

事件是C#馈赠给我们的礼物,是将委托的多播功能进行封装后的工具类

public delegate void EventHandler(object? sender, EventArgs e);
  • 事件对应的委托,不应该被类外界调用,只能由某个操作触发
  • 事件对应的委托,不应该被类外直接赋值,只能够通过+、-增减委托方法

Event事件规则

  • 加event关键字修饰的委托,只能够定义在某个类中
  • 加event关键字修饰的委托,只能够被当前类内方法触发执行;类外不触发执行
namespace Demo03
{
    class Player
    {
        // 定义Player内部会被触发的事件委托
        // 使用微软定义好的委托EventHandler
        // 使用event修饰委托引用,则类外无法直接调用OnAttach,只能进行方法加减的操作
        public event EventHandler OnAttach = null;

        public void DoAOE()
        {
            if (OnAttach != null)
            {
                OnAttach(this, EventArgs.Empty);
            }
        }
    }

    class Enemy
    {
        public void AttackMe(object? sender, EventArgs e)
        {
            Console.Write("我被攻击了");
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            var player = new Player();
            var enemy = new Enemy();

            // 将AttachMe方法注入到OnAttach委托引用中
            player.OnAttach += enemy.AttackMe;

            player.DoAOE();
        }
    }
}

事件参数EventArgs

系统提供了封装好的多播委托(事件)EventHandler,并可通过event关键字将其保护起来

public delegate void EventHandler(object? sender, EventArgs e);

但这个方法的参数是object? sender:方法发起者

还有EventArgs e:事件参数

可以看到,这个事件参数的类型是一个被定义好的类型,但实际开发过程中,我们应该使用的是自定义参的参数类型


定义自己的事件参数包class
class MyArgs
{
    public int attack = 0;
    public bool poisoned = false;
    public bool headache = false;
}

将事件参数包类型作为委托的泛型传递

可以看到源码中由此定义

public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e);

其中,我们可以自定义事件参数类型,通过泛型来实现

public event EventHandler<MyArgs> OnAttach;

响应方法中,将事件参数包替换成自定义参数包类型

在事件发起者中,将事件参数包的参数写好

被加入到事件中的方法的参数也要使用参数包类型

internal class Player
{
    public event EventHandler<MyArgs> OnAttach;

    public void DoAOE()
    {
        if (OnAttach != null)
        {
            var myArgs = new MyArgs();
            myArgs.attack = 10;
            myArgs.poisoned = true;
            myArgs.headache = true;

            OnAttach(this, myArgs);
        }
    }
}

internal class Enemy
{
    public void AttackMe(object? sender, MyArgs e)
    {
        Console.WriteLine($"{sender}发起了攻击"); // Demo04.Player发起了攻击
        Console.WriteLine($"受到了{e.attack}点伤害"); // 受到了10点伤害
        Console.WriteLine($"是否中毒:{e.poisoned}"); // 是否中毒:True
        Console.WriteLine($"是否眩晕:{e.headache}"); // 是否眩晕:True
    }
}

事件总结

事件是一个public类型的字段,由开发者在类中使用event关键字定义此字段

通常我们使用系统封装的EventHandler多播委托作为事件的字段定义,这个字段拥有两个参数,分别是事件发起者和事件参数包

通常我们使用自定义事件参数包(class)并通过泛型的方式使用它

在调用事件时,我们加入到此字段的方法的返回值和参数应该与事件字段的定义相同

然后将方法使用+=加入到这个字段中,再通过封装好的方法调用字段(其本质是调用加入的方法)

需要注意的是:如果使用event关键字保护了字段,这里不能直接使用调用字段的方式调用方法,需要额外的封装一个方法(例如(OnClick)来调用字段(也就是方法))

发布-订阅模式通过引入中间件(消息代理)来实现发布者和订阅者之间的解耦,使得系统更加灵活和可扩展。这种模式在许多领域都有广泛的应用,包括消息队列、事件驱动架构和分布式系统等。