Unity/C#------委托与事件(一篇文章彻底搞懂...)

这篇具有很好参考价值的文章主要介绍了Unity/C#------委托与事件(一篇文章彻底搞懂...)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一:委托

        所有的代码语言创造者母语都是英语,我们从英语翻译到中文的过程中难免会存在一些不太能还原本意的词,比如我之前一直不理解构造函数和析构函数,只知道这俩货作用相反,直到我看到了它的英文意思,Construstor/Distructor,我才彻底理解了他们的作用。

        接下来我们来看委托,Delegate,来看两个例句,深入理解Delegate...

Can you delegate some tasks or projects?                                你能够分配一些任务或者项目吗?

So why not delegate more work to your employees?    所以你为啥不给你的员工多分分配点任务?

从上面的句子中我们可以看到,他就是分配,也就是委托的意思(但是感觉可能有些人对委托的理解不如分配来的直接,至少对我来说是这样)

微软官方的解释是委托可以获取一个或多个方法,但是类型和返回值必须和方法相同,可以理解成委托是方法的抽象,也就是说定义一个方法的模板,至于这个方法具体是怎么样的,就由方法自己去实现。这点和函数指针很像...后续写单播委托时候再添加这部分内容。

我们可以说,委托就是一个方法的类型

话不多说,看代码...

一、单播委托——一次只能装进去一个方法

Public delegate 返回值 MyDelegate(参数1,参数2)

就这么一步,我们就把委托定义出来了,接下来要做的就是把这玩意儿实例化出来,那我们怎么把我们的方法给委托呢?

第一点就是我们需要有和定义的委托类型一致的返回值和参数列表

其实委托就是起源于C语言的函数指针,不过在C#以委托的形式存在了

但是在Java中没有委托这么一说...

//使用Typedef将该函数指针声明为一种类型,它是指向两个参数为int,返回值为int的函数指针
typedef int (*Calculator)(int x , int y);

int Add(int a ,int b)
{
    return a+b;
}

int Multiply(int a ,int b)
{
    return a*b;
}

//函数指针的使用

Calculator Pointer1 = &Add;
Calculator Pointer2 = &Multiply;

//这样我们在调用函数的时候就不再写函数,而是采用函数指针的方法,间接的指向了该类型的函数
Pointer1(0,1);
Pointer2(1,2);

从上面的函数指针我们可以看出,我们在注册方法的时候,可以间接的声明一个和该方法类型和返回值都一致的指针类型,从而调用函数指针即可,那么我们的委托和它是类似的....

接下来看我们的委托的实例化和方法的插入,可以使用new,也可以直接引用方法:

//实例化委托
MyDelegate myDelegate = new MyDelegate(Function);

//简化写法
myDelegate = Telegate;



返回值类型 Function(参数1,参数2)
{
    方法体;
}

委托的调用,可以使用Invoke,也可以直接写委托名+()

可以通过Invoke进行调用委托
myDelegate.Invoke();

也可以直接myDelegate();

二、多播委托——一次装多个方法,但不安全

在上面的方法添加环节,我们只需要做小小的修改

myDelegate += ChangeColor;
myDelegate += Log;

但是其实我们没事儿也不会这么干,这样长期下来有可能会存在内存泄漏,如果顺序执行列表中方法有一个出错了,后面的就都不会执行了,所以我们还有其他更好的选择

三、Action委托和Func委托

大多数的情况下,我们不太需要自己去声明委托,而是使用现成的委托即可,Unity为我们内置了两种泛型委托

1)Action委托——返回值必须为空,参数可有可无

//声明无参数的Action委托
Action action;

//声明有参数的Action委托
Action<string,float> action1;

//Action的使用
action = new Action(同参数的方法1)
action1 = new Action<string ,float> (sayhello);

//sayhello方法
public void SayHello(string name,float num)
{
    Debug.log(sting.Fromat("{0} has {1} ChampionShips .",name,num));
}

2) Func委托——返回值必须有,但是参数可有可无

//声明Func委托,前面是参数,后面是返回值
Func<double,double,double> func1;

//使用
func1 = new Func<double,double,double>(Add);


//跟Func结构一样的方法
public double Add(doublea ,double b)
{
    return a+b;
}

二:事件

事件,使对象或类具备通知能力

事件,是委托字段的一个包装器,它对于委托字段的访问起了限制作用

对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能

日常开发中,自己声明事件的机会比较少,一般是用已有事件比较多...

java中没有委托,事件这么一说,只用Interface来实现

一、事件的五个重要因素

事件的拥有者

事件成员(Event)

事件的响应者(Event Subscriber)

事件处理器(Event handler,本质上是回调方法)

事件订阅(+=)

他们之间的关系可以如下几种:

1:事件的拥有者类和事件的响应者是不同的类

unity 委托事件,Unity引擎,C#,unity,c#

using System.Timers;
using UnityEngine;

public class EventTimothyLiu1 : MonoBehaviour
{
    private void Start()
    {
        //世间间隔,每过1s就触发Elesap事件
        Timer timer = new Timer();
        timer.Interval = 1000;

        Boy boy = new();
        Girl girl = new();

        timer.Elapsed += boy.OnAction;
        timer.Elapsed += girl.OnAction;

        timer.Start();
    }
}
public class Boy
{
    internal void OnAction(object sender, ElapsedEventArgs e)
    {
        Debug.Log("1");
    }
}
public class Girl
{
    internal void OnAction(object sender, ElapsedEventArgs e)
    {
        Debug.Log("2");
    }
}

在该示例中,事件的拥有者是Timer类,事件的响应者是自定义的Boy,Girl类

所以事件的响应者和拥有者是两个类

第二个例子:

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program
    {
        static void Main(string[] args)
        {
            //事件的拥有者:Form
            Form form = new Form();
            //事件的响应者:Controller
            Controller controller = new Controller(form);
            form.ShowDialog();
        }
    }
    class Controller
    {
        private Form form;
        /// <summary>
        /// CTOR再加Tab即可编写出构造器
        /// 也叫构造函数,每个类都必须有
        /// 在构建类的引用时自动运行的方法!
        /// </summary>
        public Controller(Form form)
        {
            if (form!= null)
            {
                //this就是类的实例
                //this后是我们定义的字段,后面是参数form
                this.form = form;
                //Click是事件
                this.form.Click += this.FormClicked;
            }
        }
        /// <summary>
        /// 事件处理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FormClicked(object sender, EventArgs e)
        {
            this.form.Text = DateTime.Now.ToString();
        }
    }
}

上述的代码段中,我们引入了Form名称空间

事件的拥有者是Form,事件的响应者是Controller

2:事件的拥有者同时也是事件的响应者

unity 委托事件,Unity引擎,C#,unity,c#

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program1
    {
        static void Main(string[] args)
        {
            //事件的拥有者:myForm
            //事件的接受者:myForm
            MyForm myForm = new MyForm();
            //事件:Click
            myForm.Click += myForm.FormClicked;
        }
    }
    /// <summary>
    /// 继承Form
    /// </summary>
    class MyForm : Form
    {
        /// <summary>
        /// 事件的处理器
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        internal void FormClicked(object sender, EventArgs e)
        {
            this.Text = DateTime.Now.ToString();
        }
    }
}

事件的拥有者是Form

事件的响应者也是myForm的实例

3:事件的拥有者是事件的响应者的成员(频率最高)

事件的响应者用自己的方法订阅者自己的字段成员的事件

unity 委托事件,Unity引擎,C#,unity,c#

using System;
using System.Windows.Forms;

namespace EventLiu
{
    class Program1
    {
        static void Main(string[] args)
        {
            MyForm myForm = new MyForm();
            myForm.ShowDIalog();
        }
    }
    class MyForm :Form
    {
        private TextBox textBox;
        //从订阅看事件拥有者就是button
        private Button button;

        public MyForm()
        {
            this.textBox = new TextBox();
            this.button = new Button();
            this.Controls.Add(this.button);
            this.Controls.Add(this.textBox);

            //Click是事件
            //事件的响应者是this,也就是MyForm的实例对象
            this.button.Click += this.ButtonClicked;
        }
        //事件处理器
        private void ButtonClicked(object sender, EventArgs e)
        {
            this.textBox.Text = "Hello";
        }
    }
}

在该段代码中,我们自己创建MyForm类,继承自Form

事件的拥有者是该类中的成员Button,事件的响应者是该类的实例化

也就是儿子有事件,爸爸订阅了。                                                                                                                                           

                                                                                                                                                                                                                                                                                 

4:事件的响应者是事件的拥有者的成员

unity 委托事件,Unity引擎,C#,unity,c#

二、事件的完整声明格式

用以下代码来看:

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu2 : MonoBehaviour
{
    private void Start()
    {
        Customer1 customer1 = new();
        Waiter1 waiter1 = new();

        customer1.Order += waiter1.Action;
        customer1.Action();
        customer1.PayTheBill();
    }
}
/// <summary>
/// 派生自EventArgs,习惯
/// </summary>
public class OrderEventArgs1:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}

//把委托放置在类外
/// <summary>
/// 用EvenetHandler原因:
/// 1:别人看到这个后缀就知道这个委托是专门用来声明事件的
/// 2:EventHandle表明委托来约束事件处理器
/// 3:委托的实例是用来存储事件处理器
/// </summary>
/// <param name="customer"></param>
/// <param name="e"></param>
public delegate void OrderEventHandler1(Customer1 customer, OrderEventArgs1 e);


/// <summary>
/// Customer1是事件的拥有者,它拥有事件Order
/// </summary>
public class Customer1
{
    private OrderEventHandler1 OrderEventHandler1;
    public event OrderEventHandler1 Order
    {
        add { this.OrderEventHandler1 += value; }
        remove { this.OrderEventHandler1 -= value; }
    }

    public double Bill { get; set; }

    public void PayTheBill()
    {
        Debug.Log("I will pay $" + this.Bill);
    }

    public void WalkIn()
    {
        Debug.Log("Walk into the restaurant.");
    }
    public void SitDown()
    {
        Debug.Log("Sit down.");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Let me think...");
            Thread.Sleep(1000);
        }
        if(this.OrderEventHandler1 != null)
        {
            OrderEventArgs1 e = new OrderEventArgs1
            {
                DishName = "Kongpao Chicken",
                size = "large"
            };
            this.OrderEventHandler1.Invoke(this,e);
        }
    }
    public void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }
}

/// <summary>
/// Waiter1是事件的接受者,它拥有事件的响应器Action
/// </summary>
public class Waiter1
{
    public void Action(Customer1 customer1, OrderEventArgs1 e)
    {
        Debug.Log("I will serve you  the dish -{0} "+ e.DishName);
        double price = 10;
        switch (e.size)
        {
            case "small":
            price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer1.Bill += price;
    }
}

从以上代码可以看出

Customer进入餐厅,走进,坐下,思考,然后触发点餐事件

Customer是点餐事件的拥有者

Waiter是点餐事件的接受者,它还得有点餐的响应事件,就是算出价格,并采取行动

在自定义点餐事件的时候,我们采用了EventHandler委托来给事件做支撑

创建了EventArgs类来承载事件,谁发起的事件,谁就是委托的第一个参数

但是这样写比较复杂,我们来看事件的简略声明格式

三、事件的简单声明格式

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu3 : MonoBehaviour
{
    private void Start()
    {
        Customer2 customer2 = new();
        Waiter2 waiter2 = new Waiter2();

        customer2.Order2 += waiter2.Action;

        customer2.Action();
        customer2.PayTheBill();
    }
}
/// <summary>
/// 先声明委托,委托两个参数分别是事件的拥有者和事件需要使用的参数
/// </summary>
/// <param name="customer2"></param>
/// <param name="e"></param>
public delegate void OrderEventHandler2(Customer2 customer2, OrderEventArgs2 e);

/// <summary>
/// 容纳事件参数
/// </summary>
public class OrderEventArgs2:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}

/// <summary>
/// 事件的拥有者
/// </summary>
public class Customer2
{
    //原本需要先实例化委托,然后将委托赋予事件中进行使用
    //private OrderEventHandler1 OrderEventHandler1;
    //public event OrderEventHandler1 Order
    //{
    //    add { this.OrderEventHandler1 += value; }
    //    remove { this.OrderEventHandler1 -= value; }
    //}
    //现在进行简化
    /// <summary>
    /// 只需要事件+委托即可
    /// </summary>
    public event OrderEventHandler2 Order2;

    public double Bill { get; set; }
    public void PayTheBill()
    {
        Debug.Log("I will pay $ " + this.Bill);
    }
    public void WalkIn()
    {
        Debug.Log("I'm Coming");
    }
    public void SitDown()
    {
        Debug.Log("I'm sit down");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Let me think..");
            Thread.Sleep(1000);
        }
        if(Order2 != null)
        {
            OrderEventArgs2 e = new OrderEventArgs2();
            e.DishName = "Kongpao Chicken";
            e.size = "large";
            this.Order2.Invoke(this, e);
        }
    }
    public void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }
}
/// <summary>
/// 事件的接收者和Action事件
/// </summary>
public class Waiter2
{
    public void Action(Customer2 customer2, OrderEventArgs2 e)
    {
        Debug.Log("I will serve you  the dish: " + e.DishName);
        double price = 10;
        switch (e.size)
        {
            case "small":
                price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer2.Bill += price;
    }
}

还是上述的代码,我们进行了简化

我们将如下代码:

private OrderEventHandler1 OrderEventHandler1;
    public event OrderEventHandler1 Order
    {
        add { this.OrderEventHandler1 += value; }
        remove { this.OrderEventHandler1 -= value; }
    }

简化成了:

public event OrderEventHandler2 Order2

这样的简化是微软后台进行了,我们可以直接如此书写

其实并非不存在委托字段的声明,还是存在的,只是存在于后台中,微软后台声明了,我们不需要看到,所以可以进行简化

还可以进一步简化:

将声明委托的部分省略掉,即使用微软定义好的EventHandler的委托类型,代码如下:

using System;
using System.Threading;
using UnityEngine;

public class EventTimothyLiu4 : MonoBehaviour
{
    private void Start()
    {
        Customer3 customer3 = new Customer3();
        Waiter3 waiter3 = new Waiter3();

        customer3.Order3 += waiter3.Action;

        customer3.Action();
        customer3.PayTheBill();
    }
}
public class OrderEventArgs3:EventArgs
{
    public string DishName { get; set; }
    public string size { get; set; }
}
public class Customer3
{
    public event EventHandler Order3;
    public double Bill { get; set; }

    public void WalkIn()
    {
        Debug.Log("Im coming");
    }
    public void SitDown()
    {
        Debug.Log("Im Sitting");
    }
    public void Think()
    {
        for (int i = 0; i < 5; i++)
        {
            Debug.Log("Im thinking");
            Thread.Sleep(1000);
        }
        if(Order3 != null)
        {
            OrderEventArgs3 e = new OrderEventArgs3();
            e.DishName = "KongpaoChicken";
            e.size = "large";
            this.Order3.Invoke(this, e);
        }
    }
    internal void Action()
    {
        this.WalkIn();
        this.SitDown();
        this.Think();
    }

    internal void PayTheBill()
    {
        Debug.Log("I will Pay for $" + this.Bill);
    }
}
public class Waiter3
{
    public void Action(object sender,EventArgs e)
    {
        Customer3 customer3 = sender as Customer3;
        OrderEventArgs3 orderinfo = e as OrderEventArgs3;

        Debug.Log("i will serve  you the dish" + orderinfo.DishName);
        double price = 10;
        switch (orderinfo.size)
        {
            case "small":
                price = price * 0.5;
                break;
            case "large":
                price = price * 1.5;
                break;
            default:
                break;
        }
        customer3.Bill += price;
    }
}

注意在Waiter类中的实例化Cutsome3和OrderEventArgs3的实例化使用

采用实例化,参数 as 的方法,将参数转化为我们需要的

为什么有了委托字段/属性,还需要事件?

为了程序逻辑更加有道理,更加安全,谨防“借刀杀人”文章来源地址https://www.toymoban.com/news/detail-737586.html

三:UnityEvent/UnityAction

到了这里,关于Unity/C#------委托与事件(一篇文章彻底搞懂...)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Unity自制手册】unity常用API大全——一篇文章足以(万字详解)

    👨‍💻个人主页 :@元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏 unity 实战系列 ⭐相关文章⭐ ⭐【软件设计师高频考点暴击】 -本站最全-unity常用API大全(万字详解),不信你不收藏 -关于游戏剧情模式中用到的基础简单API -控制游

    2024年02月12日
    浏览(32)
  • 目标检测扩(六)一篇文章彻底搞懂目标检测算法中的评估指标计算方法(IoU(交并比)、Precision(精确度)、Recall(召回率)、AP(平均正确率)、mAP(平均类别AP) )

    ​ 基本在目标检测算法中会碰到一些评估指标、常见的指标参数有:IoU(交并比)、Precision(精确度)、Recall(召回率)、AP(平均正确率)、mAP(平均类别AP)等。这些评估指标是在评估阶段评价训练的网络好坏的重要依据。 计算方法 IoU: 用来评价目标检测算法的对象定

    2024年04月13日
    浏览(33)
  • 【unity本站最全系列】unity常用API大全一篇文章足以(万字详解)不信你不收藏

    👨‍💻个人主页 :@元宇宙-秩沅 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 本文由 秩沅 原创 收录于专栏 unity 实战系列 ⭐相关文章⭐ ⭐【软件设计师高频考点暴击】 -本站最全-unity常用API大全(万字详解),不信你不收藏 -关于游戏剧情模式中用到的基础简单API -控制游

    2024年02月07日
    浏览(40)
  • 一篇文章搞懂Docker、DockerCompose

    大型项目组件较多,运行环境也较为复杂,部署时会碰到一些问题: 依赖关系复杂,容易出现兼容性问题 开发、测试、生产环境有差异 例如一个项目中,部署时需要依赖于node.js、Redis、RabbitMQ、MySQL等,这些服务部署时所需要的函数库、依赖项各不相同,甚至会有冲突。给部

    2024年01月18日
    浏览(35)
  • 一篇文章彻底清楚shellcode(精品)

    1.没开沙箱(此时我们可以系统调用get shell) (一)32位程序系统调用 32位程序有别于64位程序,32位通过栈传参,我们常用的寄存器有4个数据寄存器(eax,ebx,ecx,edx),2个变址寄存器(esi,edi),2个指针寄存器(esp,ebp). 下边我们就来看一种系统调用方式及其构造: 执行上述shellcode即可g

    2024年02月09日
    浏览(30)
  • 一篇文章搞懂Git与Github

    Git 是一个开源的 分布式版本控制系统 ,Github 是 全球最大的同性交友网站 基于 Git 的 代码托管平台 ,因为只支持 Git 作为 唯一的版本库 格式进行托管,故名 GitHub,就是一个平台上面有无数个 Git 仓库——Git 版的百度云,承担存储远程仓库的作用。 唯一版本库是什么意思?

    2024年02月06日
    浏览(33)
  • 一篇文章让你搞懂内存函数

    库函数memcmp介绍 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 这个函数在遇到 ‘\\0’ 的时候并不会停下来。 如果source和destination有任何的重叠,复制的结果都是未定义的。 库函数memcmp的代码形式 看代码 memcmp将arr1中的内容拷贝到arr2中,总共

    2024年02月17日
    浏览(33)
  • 一篇文章搞懂华为的ACL

    随着网络的飞速发展,网络安全问题日益突出。访问控制列表 (ACL, Access Control List) 可以通过对网络中报文流的精确识别,与其他技术结合,达到控制网络访问行为、防止网络攻击和提高网络带宽利用率的目的,从而切实保障网络环境的安全性和网络服务质量的可靠性。 访问

    2024年02月06日
    浏览(32)
  • 一篇文章,带你彻底掌握接口测试!

    一、什么是接口测试? 所谓接口,是指同一个系统中模块与模块间的数据传递接口、前后端交互、跨系统跨平台跨数据库的对接。而接口测试,则是通过接口的不同情况下的输入,去对比输出,看看是否满足接口规范所规定的功能、安全以及性能方面的要求。 二、为什么要

    2024年02月10日
    浏览(35)
  • 一篇文章彻底理解自定义View

    目录 一.View的基础  1.view的基础概念 2.view的位置和事件event几种表示法 3.view的滑动

    2024年02月01日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包