网络知识 娱乐 Unity中关于委托与事件的使用及区别

Unity中关于委托与事件的使用及区别

一、前言

1.什么是委托?

委托是一种容器,容器里面放的是函数方法。而函数的形式各不相同,参数,返回值各不相同,所以你做委托之前,先得要定义好这个委托容器存放的函数的类型,即委托类型。

定义了好了函数类型后,将函数加入到委托容器后,你只要触发委托调用,委托就会帮你把容器里面的每个函数都调用一次,触发的时候和调用普通函数没有区别。

2.什么是事件?

事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

二、简单案例介绍解析

1.委托

举个例子来使用委托:我们先定义一个宠物名称委托,在TestA类中实现狗和猫的方法

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

public delegate void PetName(string name); //委托 (需要一个string类型的参数当宠物名称)

//陆地动物类
public class TestA : MonoBehaviour
{
    PetName petName; //声明委托

    void Start()
    {
        //委托
        petName += Dog; //狗
        petName += Cat; //猫

        //调用委托事件
        petName("大白");

    }

    //狗 因为委托调用所以需要一个string参数
    void Dog(string name)
    {
        Debug.Log($"我叫{name},是一只猫");
    }

    //猫
    void Cat(string name)
    {
        Debug.Log($"我叫{name},是一只狗");
    }
}

运行结果:

从中我们可以看到,当我们把两个方法(狗,猫)“添加”到委托中时,我们只需要调用定义的委托(petName方法) 就可以同时执行添加的两个方法。根据上面的委托的含义来解释,我们创建了一个petName委托当作容器,然后将dog(狗),cat(猫)方法添加到容器中,这样我们只需要执行容器就可以将容器中的所有方法都给执行。

2.事件

 需要基于上一个委托来实现,我们先创建一个TestB类,里面有两个方法Parrot(string name)鹦鹉、Pigeon(string name)鸽子。同时创建一个Event事件。

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

//鸟类
public class TestB : MonoBehaviour
{
    public static event PetName SayHelloEvent;//事件

    void Start()
    {
        //在事件中添加方法
        SayHelloEvent = Parrot;
        SayHelloEvent += Pigeon;

        SayHelloEvent("小白");
    }

    //鹦鹉
    public static void Parrot(string name) 
    {
        Debug.Log($"我叫{name},是一只鹦鹉");
    }

    //鸽子
    public static void Pigeon(string name)
    {
        Debug.Log($"我叫{name},是一只鸽子");
    }
}

把上一个脚本TestA禁用 将TestB拖入场景运行效果:

三、委托与事件的区别

 委托与事件的使用是一样的,但是事件会有很多限制条件。两者定义与区别如下:

1.委托创建时会定义方法的类型。(是否有无参数、有无返回值)

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

public delegate void DelegateA();               //无参无返回值
public delegate void DelegateB(string name);    //有参无返回值
public delegate string DelegateC();             //无参有返回值
public delegate string DelegateD(string name);  //有参有返回值

//陆地动物类
public class TestA : MonoBehaviour
{
    //委托定义
    DelegateA delegateA;        //无参无返回值
    DelegateB delegateB;        //有参无返回值
    DelegateC delegateC;        //无参有返回值        
    DelegateD delegateD;        //有参有返回值

    void Start()
    {
        //添加方法
        delegateA = A;
        delegateB = B;
        delegateC = C;
        delegateD = D;

        //这里错误示范  delegateA委托添加B方法会报错,原因委托定义了方法类型,B方法类型不符合delegateA定义的方法类型会报错
        //delegateA += B;

        //运行
        delegateA();
        delegateB("");
        delegateC();
        delegateD("");

    }

    void A() 
    {
        Debug.Log("无参无返回值");
    }
    void B(string name)
    {
        Debug.Log("有参无返回值");
    }
    string C()
    {
        Debug.Log("无参有返回值");
        return "";
    }
    string D(string name)
    {
        Debug.Log("有参有返回值");
        return "";
    }
}

运行结果:

 

错误示范:

2.事件的创建需要一个委托才能声明,

3.委托可以在任何类中或类外声明,但是事件只能在类中声明,如下:

4.事件只能在当前声明的类中使用,无论将事件设置成public还是static其他类都无法调用

 总的来说事件Event是一个添加了许多限制的委托。

 四、案例使用

案例一:

举一个大家经常用的例子,你在CSDN中订阅一个博主的专栏(游戏专栏),当这个博主发布了这个专栏的一个最新文章,就会收到消息。这个就是用的委托,实现如下

1.创建一个博主类Blogger,负责书写文章,并且调用委托事件向订阅专栏的用于发送最新文章

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


//这是一个博主类负责书写文章,并且调用委托事件向订阅专栏的用于发送最新文章
public class Blogger : MonoBehaviour
{
    public InputField articleName; //文章名称
    public Button uploadBtn; //上传文章

    void Start()
    {
        //按钮监听
        uploadBtn.onClick.AddListener(UpLoad);
    }

    //上传事件
    void UpLoad() 
    {
        //上传文章名称不为空时
        if (articleName.text!=string.Empty)
        {
            Debug.Log($"博主上传文章:{articleName.text}");
        }
    }
}

 2.创建一个有参无返回值的订阅委托,用于存放订阅人员推送信息方法,并在博主类中声明创建。与上方博主类结合,完整代码如下:

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

public delegate void Subscribe(string name);//订阅委托 用于存放订阅人员的推送方法

//这是一个博主类负责书写文章,并且调用委托事件向订阅专栏的用于发送最新文章
public class Blogger : MonoBehaviour
{
    public InputField articleName; //文章名称
    public Button uploadBtn; //上传文章

    public static Subscribe subscribe; //创建公用的静态订阅委托

    void Start()
    {
        //按钮监听
        uploadBtn.onClick.AddListener(UpLoad);
    }

    //上传事件
    void UpLoad() 
    {
        //上传文章名称不为空时
        if (articleName.text!=string.Empty)
        {
            Debug.Log($"博主上传文章:{articleName.text}");
            subscribe(articleName.text); //向订阅的人员发送信息
        }
    }
}

3.新建两个PeopleA、PeopleB人员类,在里面声明Push推送消息方法,并且在strat里添加到订阅委托中 

PeopleA:

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

//订阅人员A
public class PeopleA : MonoBehaviour
{
    void Start()
    {
        Blogger.subscribe += Push; //向订阅委托中添加人员A的推送方法
    }

    //推送信息
    void Push(string name)
    {
        Debug.Log($"我是人员A,接收到博主最新文章:{name}");
    }
}

PeopleB:

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

//订阅人员B
public class PeopleB : MonoBehaviour
{
    void Start()
    {
        Blogger.subscribe += Push; //向订阅委托中添加人员A的推送方法
    }
    //推送信息
    void Push(string name) 
    {
        Debug.Log($"我是人员B,接收到博主最新文章:{name}");
    }
}

将这三个脚本拖入场景布局如下:

 运行结果:

其他案例:

https://blog.csdn.net/ChinarCSDN/article/details/80387157?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522161232462616780255284165%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=161232462616780255284165&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-80387157.first_rank_v2_pc_rank_v29_10&utm_term=unity%E4%B8%AD%E5%AE%9A%E4%B9%89delegate%E5%A7%94%E6%89%98%E5%9C%A8%E5%85%B6%E4%BB%96%E8%84%9A%E6%9C%AC%E4%B8%AD%E8%B0%83%E7%94%A8&spm=1018.2226.3001.4187