【unity 代码升华篇】委托、事件全解析(一)

22 篇文章 1 订阅
1.前言
最近因为工作事情以及个人原因,搞得心情烦闷,哎...“程序是寂寞的,只有程序员能懂“她”,可惜能懂程序员的人确很少很少”
呵呵...请容许我在此小小的牢骚下.
帖子也是许久未曾更新了,首先,在此感谢大家一直对我的关注,
看过我前面帖子人大概比较清楚,我个人比较侧重代码优化、代码管理等.
所以,酝酿许久,在此还是送上一套与代码有关的东西吧!
毕竟作为程序的我们也只有”她“对你不离不弃了 ,呵呵... 
后面,我会以委托、事件作为背景, 由浅入深地讲述什么是委托、 为什么要使用委托、事件的由来、 .NET Framework  中的委托和事件、
委托中方法异常和超时的处理、委托与异步编程、委托和事件对 Observer  设计模式的意义等。
为了大家更快、更好、更细的掌握,都会给出对应实例。以后 坚持每天一更(至少这个系列),让大家也好有个消化时间,
也不搞回复可见了,要是大家觉得好、有帮助就给个赞。
当然,有疑问或有更好的方式都欢迎留言。ok,牢骚发完了,现在进入正题。

2.理解委托
通过本篇大家会学会利用委托方法来做代码提升.
具体如下:
一、 将方法作为方法的参数
我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是Debug 输出一句问候的话语:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;
using System.Collections;
public class DelegateTest : MonoBehaviour
{
     public UIButton _btn;
     void Awake()
     {
         //使用EventDelegate注册按钮事件
         EventDelegate.Add( this ._btn.onClick, this .BtnClicked);
     }
     private void BtnClicked()
     {
         GreetPeople( "Jack" );
     }
     public void GreetPeople( string name)
     {
         // 做某些额外的事情,比如初始化之类,此处略
         EnglishGreeting(name);
     }
     public void EnglishGreeting( string name)
     {
         Debug.Log( "Good Morning, " + name);
     }
}

代码很简单,暂且不管这两个方法有没有什么实际意义。
GreetPeople(string name)用于向某人问好,当我们传递代表某人姓名的name 参数,比如说“Jack ”,进去的时候,
在这个方法中,将调用EnglishGreeting方法,再次传递name 参数,EnglishGreeting 则输出 “Good Morning, Jack  ”。
 
现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“GoodMorning”是什么意思,怎么办呢?
好吧,我们再加个中文版的问候方法:
[C#]  纯文本查看  复制代码
?
1
2
3
4
public void ChineseGreeting( string name)
{
     Console.WriteLine( "早上好, " + name);
}

这时候,GreetPeople 也需要改一改了,不然如何判断到底用哪个版本的Greeting 问候方法合适呢?
在进行这个之前,我们最好再定义一个枚举作为判断的依据:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public enum Language
{
     English, Chinese
}
public void GreetPeople( string name, Language lang)
{
     //做某些额外的事情,比如初始化之类,此处略
     swith(lang)
     {
         case Language.English:
             EnglishGreeting(name);
             break ;
         case Language.Chinese:
             ChineseGreeting(name);
             break ;
     }
}
OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可扩展性很差,
如果日后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。
在考虑新的解决方案之前,我们先看看上面 GreetPeople 的方法签名:
//================================
public void GreetPeople(string name, Language lang)
//================================
如果大家再仔细想想,假如GreetPeople()方法 可以接受一个参数变量,这个变量可以代表另一个方法
当我们给这个变量赋值 EnglishGreeting 的时候,它代表着 EnglsihGreeting() 这个方法;
当我们给它赋值ChineseGreeting 的时候,它又代表着ChineseGreeting()方法。
so,在此我们将这个参数变量命名为 MakeGreeting,那么不是可以如同给name 赋值时一样,在调用GreetPeople()方法的时候,
给这个MakeGreeting参数也赋上值么(ChineseGreeting或者EnglsihGreeting等)?
然后,我们在方法体内,也可以像使用别的参数一样使用MakeGreeting。但是,由于MakeGreeting 代表着一个方法,
它的使用方式应该和它被赋的方法(比如ChineseGreeting)是一样的,
比如:
//=============
MakeGreeting(name);
//=============
好了,有了思路了,我们现在就来改改GreetPeople()方法,那么它应该是这个样子了:
//====================================
public void GreetPeople(string name, xxx MakeGreeting)
{
    MakeGreeting(name);
}
//=====================================
注意到  xxx  ,这个位置通常放置的应该是参数的类型,但到目前为止,我们仅仅是想到应该有个可以代表方法的参数,
并按这个思路去改写GreetPeople 方法,现在就出现了一个大问题:这个代表着方法的MakeGreeting 参数应该是什么类型的?
说 明:这里已不再需要枚举了,因为在给MakeGreeting 赋值的时候动态地决定使用哪个方法,是ChineseGreeting 还是 EnglishGreeting,而在这个两个方法内部,已经对使用“Good Morning”还是“早上好”作了区分。
聪明的你应该已经想到了,现在是委托该出场的时候了,但讲述委托之前,我们再看看MakeGreeting 参数所能代表的 ChineseGreeting()和EnglishGreeting()方法的签名:
//=========================
public void EnglishGreeting(string name)
public void ChineseGreeting(string name)
//=========================
如同name 可以接受String 类型的“true”和“1”,但不能接受bool 类型的true 和int 类型的1 一样。
MakeGreeting的 参数类型定义应该能够确定 MakeGreeting 可以代表的 方法种类,再进一步讲,就是MakeGreeting 可以代表的方法 的 参数类型和返回类型。
于是,委托出现了:它定义了MakeGreeting 参数所能代表的方法的种类,也就是MakeGreeting参数的类型。
说 明:如果上面这句话比较绕口,我把它翻译成这样:string 定义了name 参数所能代表的值的种类,也就是name 参数的类型。
本例中委托的定义:
//================================
public delegate void GreetingDelegate(string name);
//================================
可以与上面EnglishGreeting()方法的签名对比一下,除了加入了delegate 关键字以外,其余的是不是完全一样?
现在,让我们再次改动GreetPeople()方法,如下所示:
[C#]  纯文本查看  复制代码
?
1
2
3
4
public void GreetPeople( string name, GreetingDelegate MakeGreeting)
{
     MakeGreeting(name);
}
如你所见,委托GreetingDelegate 出现的位置与 string 相同,string 是一个类型,那么GreetingDelegate 应该也是一个类型,或者叫类(Class)。
但是委托的声明方式和类却完全不同,这是怎么一回事?实际上,委托在编译的时候确实会编译成类。
因为 Delegate  是一个类所以在任何可以声明类的地方都可以声明委托
更多的内容将在后面讲述,现在,请看看这个范例的完整代码:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using UnityEngine;[/align]
using System.Collections;
 
//定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate( string name);
public class DelegateTest : MonoBehaviour
{
     public UIButton _btn;
     void Awake()
     {
         //使用EventDelegate注册按钮事件
         EventDelegate.Add( this ._btn.onClick, this .BtnClicked);
     }
     private void BtnClicked()
     {
         GreetPeople( "Jack" ,EnglishGreeting);
         GreetPeople( "随风去旅行" ,ChineseGreeting);
     }
     private  void EnglishGreeting( string name)
     {
         Debug.Log( "Good Morning, " + name);
     }
     private  void ChineseGreeting( string name)
     {
         Debug.Log( "早上好, " + name);
     }
     //注意此方法,它接受一个GreetingDelegate 类型的方法作为参数
     public  void GreetPeople( string name, GreetingDelegate MakeGreeting)
     {
         MakeGreeting(name);
     }
}

运行,输出如下:
 

我们现在对委托做一个总结:
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,
可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性哟。


二、将方法绑定到委托

看到这里,是不是有那么点如梦初醒的感觉?于是,大家是不是在想:
而既然委托 GreetingDelegate  和类型  string  的地位一样,都是定义了一种参数类型,那么,我是不是也可以这么使用委托?
行,说干就干,在此修改下按钮点击事件中的代码:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
private void BtnClicked()
    {
        //GreetPeople("Jack",EnglishGreeting);
        //GreetPeople("随风去旅行",ChineseGreeting);
        GreetingDelegate delegate1, delegate2;
        delegate1 = EnglishGreeting;
        delegate2 = ChineseGreeting;
        GreetPeople( "Jack" , delegate1);
        GreetPeople( "随风去旅行" , delegate2);
    }

如你所料,这样是没有问题的,程序一如预料的那样输出。
这里,我想说的是委托不同于 string  的一个特性:可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,
当调用这个委托的时候,将依次调用其所绑定的方法。
在这个例子中,继续修改语法如下:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
     private void BtnClicked()
     {
         //GreetPeople("Jack",EnglishGreeting);
         //GreetPeople("随风去旅行",ChineseGreeting);
         GreetingDelegate delegate1;
         delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
         delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
         // 将调用 EnglishGreeting 方法,后调用 ChineseGreeting 方法
         GreetPeople( "Jack" , delegate1);
}

运行如下:
 

实际上,我们可以也可以绕过 GreetPeople  方法,通过委托来直接调用 EnglishGreeting  ChineseGreeting,修改如下:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using UnityEngine;
using System.Collections;
 
//定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate( string name);
public class DelegateTest : MonoBehaviour
{
     public UIButton _btn;
     void Awake()
     {
         //使用EventDelegate注册按钮事件
         EventDelegate.Add( this ._btn.onClick, this .BtnClicked);
     }
     private void BtnClicked()
     {
         //GreetPeople("Jack",EnglishGreeting);
         //GreetPeople("随风去旅行",ChineseGreeting);
         GreetingDelegate delegate1;
         delegate1 = EnglishGreeting; // 先给委托类型的变量赋值
         delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
         // 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
         delegate1( "Jack" );
     }
     private  void EnglishGreeting( string name)
     {
         Debug.Log( "Good Morning, " + name);
     }
     private  void ChineseGreeting( string name)
     {
         Debug.Log( "早上好, " + name);
     }
     //注意此方法,它接受一个GreetingDelegate 类型的方法作为参数
     public  void GreetPeople( string name, GreetingDelegate MakeGreeting)
     {
         MakeGreeting(name);
     }
}

说 明:
这在本例中是没有问题的,但回头看下上面GreetPeople()的定义,在它之中可以做一些对于EnglshihGreeting 和ChineseGreeting 来说都需要进行的工作,为了简便我做了省略.
注意这里,第一次用的“ = ”,是赋值的语法;第二次,用的是“ += ”,是绑定的语法。
如果第一次就使用“ += ”,将出现“使用了未赋值的局部变量”的编译错误。
 
我们也可以使用下面的代码来这样简化这一过程:
//============================================
GreetingDelegate delegate1 = newGreetingDelegate(EnglishGreeting);
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
//============================================

看到这里,应该注意到,这段代码第一条语句与实例化一个类是何其的相似,大家不禁想到,
上面第一次绑定委托时不可以使用“ += ”的编译错误,或许可以用这样的方法来避免:
//=========================================
GreetingDelegate delegate1 = newGreetingDelegate();
delegate1 += EnglishGreeting; // 这次用的是“+=”,绑定语法。
delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法
// =========================================
呵呵,但实际上,这样会出现编译错误:  GreetingDelegate”方法没有采用“0 ”个参数的重载
 
尽管这样的结果让我们觉得有点沮丧,
但是编译的提示:“没有 个参数的重载”再次让我们联想到了类的构造函数。
我知道大家一定按捺不住想探个究竟,但再此之前,我们需要先把基础知识和应用介绍完。
既然给 委托可以绑定一个方法,那么也应该有办法取消对方法的绑定,很容易想到,这个语法是“-=”,修改如下:
[C#]  纯文本查看  复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using UnityEngine;
using System.Collections;
 
//定义委托,它定义了可以代表的方法的类型
public delegate void GreetingDelegate( string name);
public class DelegateTest : MonoBehaviour
{
     public UIButton _btn;
     void Awake()
     {
         //使用EventDelegate注册按钮事件
         EventDelegate.Add( this ._btn.onClick, this .BtnClicked);
     }
     private void BtnClicked()
     {
         GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
         // 给此委托变量再绑定一个方法
         delegate1 += ChineseGreeting;
         // 将先后调用 EnglishGreeting 与 ChineseGreeting 方法
         GreetPeople( "Jack" , delegate1);
 
         delegate1 -= EnglishGreeting;
         Debug.Log( "取消对EnglishGreeting 方法的绑定" );
 
         // 将仅调用 ChineseGreeting
         GreetPeople( "随风去旅行" , delegate1);
     }
     private  void EnglishGreeting( string name)
     {
         Debug.Log( "Good Morning, " + name);
     }
     private  void ChineseGreeting( string name)
     {
         Debug.Log( "早上好, " + name);
     }
     //注意此方法,它接受一个GreetingDelegate 类型的方法作为参数
     public  void GreetPeople( string name, GreetingDelegate MakeGreeting)
     {
         MakeGreeting(name);
     }
}

运行如下:
 

T@S_91RXRA{`)2AX0V35I$K.jpg (84.51 KB, 下载次数: 0)

下载附件  保存到相册

5 小时前 上传


好了,让我们再次对委托作个总结:
使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。


委托,就先讲到这里啦,大家不妨试试?
明天同一时间更新事件     
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity中,委托(Delegate)和事件(Events)是两个关键的概念,它们被用来实现回调和异步编程,使得游戏开发中的组件间能够更加灵活地传递和处理信息。 **委托(Delegate):** 委托是一种类型,它存储对特定方法的引用。在Unity中,你可以创建一个委托类型,然后将实际的方法赋值给这个委托。当需要在某个时间点执行这个方法时,只需调用委托即可。例如,你可以创建一个委托来处理所有UI元素的点击事件: ```csharp public delegate void ButtonClickEventHandler(string buttonName); ButtonClickEventHandler clickHandler; // 在初始化或响应UI点击时,执行委托 void OnButtonClicked(string buttonName) { clickHandler(buttonName); // 调用存储的方法 } // 注册事件处理 void SubscribeButtonClick(Button button) { clickHandler = button.onClick; } ``` **事件(Events):** 事件是对特定委托的封装,通常由类或接口提供。当你想要公开某个行为的变化通知给其他组件时,你可以定义一个事件。在Unity中,`MonoBehaviour`类提供了许多内置事件,如`OnTriggerEnter`、`OnDestroy`等。例如,创建一个简单的碰撞事件: ```csharp public class ColliderComponent : MonoBehaviour { public event Action<GameObject> OnCollisionEnter; void OnCollisionEnter(Collision collision) { if (OnCollisionEnter != null) { OnCollisionEnter(collision.gameObject); } } } // 使用时 ColliderComponent myCollider; myCollider.OnCollisionEnter += OnCollisionHandler; void OnCollisionHandler(GameObject other) { // 处理碰撞 } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值