【Unity|C#】基础篇(8)——委托(Delegate)/ 事件(Event)
【学习资料】
《C#图解教程》(第13~14章):https://www.cnblogs.com/moonache/p/7687551.html
电子书下载:https://pan.baidu.com/s/1mhOmBG0
- 其他
> 委托与事件详解Part1:http://www.tracefact.net/tech/009.html
> 委托与事件详解Part2:http://www.tracefact.net/tech/029.html
【内容】
- 委托(Delegate)
- 定义:类似C++的函数指针
- 委托多播
- 委托绑定函数及执行原理
- 事件(Event)
- 定义:类似 字段与属性 的关系
- 事件访问器(add/remove)
- 为什么使用事件?
- 委托(Delegate)
【委托Delegate】
- 定义
- 类似C++的函数指针,delegate相当于是 指向某对象的某个函数,的 引用类型变量
- 委托和类一样,也需要 声明、创建、赋值
- 声明委托类型(像类一样声明一个委托类型) public delegate [函数返回值] 委托名称(参数1,参数2...);
- 创建委托变量 MyDelegate opt = new MyDelegate(AddNum); (实际绑定的是:this.AddNum)
- 重新赋值 opt = MultNum; (实际绑定的是:this.MultNum)
- 执行委托:就是执行引用的 委托函数
- 委托可以:动态修改引用的函数
// 声明:一个可以指向 返回类型为int,带2个int参数的 函数
public delegate int MyDelegate(int a, int b); // 相加函数
public int AddNum(int a, int b)
{
return (a + b);
}
// 相乘函数
public int MultNum(int a, int b)
{
return (a * b);
} void Start()
{
// 创建:指向不同函数的委托类型
MyDelegate opt = new MyDelegate(AddNum);
Debug.Log(opt(, )); // 执行委托函数: 7 // 运行时,可以改变赋值
opt = MultNum;
Debug.Log(opt(, )); // 执行委托函数: 10
}
- 委托多播
- 创建的委托类型变量 opt 可以存储 函数调用列表(绑定多个委托函数)
- 通过 "+",或"+=":添加 一个函数引用
- 通过 "-",或"-=" :移除 某个函数的引用
- 执行委托:会执行所有添加绑定的 委托函数
public delegate void MyDelegate(int a, int b); // 输出相加结果
public void PrintAddNum(int a, int b)
{
Debug.Log("PrintAddNum:" + (a + b));
}
// 输出相乘结构
public void PrintMultNum(int a, int b)
{
Debug.Log("PrintMultNum:" + (a * b));
} void Start()
{
// 创建:指向不同函数的委托类型
MyDelegate opt = new MyDelegate(PrintAddNum); // 通过+=添加委托函数
// 也可以直接相加 opt = PrintAddNum + PrintMultNum;
opt += PrintMultNum;
// 执行委托,会按添加的顺序,分别执行PrintAddNum和PrintMultNum
opt(, ); // 通过-=移除对某函数的引用
opt -= PrintAddNum;
// 再次执行委托,只执行了PrintMultNum
opt(, );
} // 输出
// PrintAddNum: 7
// PrintMultNum: 10
// PrintMultNum: 10
- 委托绑定函数及执行原理
- 绑定委托函数
- 可以通过:“=”、“+=”、“-=”、“+”、“-”,来重新给委托绑定函数,或添加删除函数
- 绑定普通函数:同时会将对象(this)引用存储起来
- 绑定静态函数:无需存储对象(this)引用
class Person
{
public delegate void MyDelegate(int a, int b);
public MyDelegate myDelegate; // 普通函数,绑定时需要有明确对象(this)
public void Func1(int a, int b) { ... }
// 静态函数,通过类名.Func2绑定
public static void Func2(int a, int b) { ... }
} void Start()
{
Person myPerson = new Person(); //myPerson.myDelegate += Person.Func1; // 报错,Func1需要对象(this)
myPerson.myDelegate += Person.Func2; myPerson.myDelegate += myPerson.Func1;
//myPerson.myDelegate += myPerson.Func2; // 报错,Func2是静态函数
}
- 执行方式
- 通过"+" 或 "+=" 的顺序,依次执行绑定的委托函数
- 普通函数:相当于 调用 绑定对象引用(this)的函数,包含了对象的this引用
- 静态函数:相当于 调用 绑定的委托函数;
// 声明委托类型
public delegate void MyDelegate(); class Person
{
public string name;
public void PrintName()
{
Debug.Log(name);
}
} void Start()
{
// 创建2个对象
Person myPerson = new Person();
Person myPerson2 = new Person();
myPerson.name = "Alice";
myPerson2.name = "Bob"; // 创建委托变量
MyDelegate myDelegate = new MyDelegate(myPerson.PrintName);
// 多播:委托函数
myDelegate += myPerson2.PrintName; // 执行委托
myDelegate();
} // 输出
// Alice
// Bob
- 绑定委托函数
【事件】
- 定义
- 事件是对委托的封装,关系和字段与属性(Property) 一样
- 事件是基于委托,所以首先要声明委托,再定义事件
// 声明委托类型
public delegate void MyDelegate(int a, int b);
// 定义事件
public event MyDelegate myEvent;- 注:事件的声明只能在类内部,不能在函数内
- 原理
- 实际上与属性一样,定义一个事件,会自动创建一个隐藏(private)的委托类型变量
- 并包含了add_xxx("+=")和remove_xxx("-="),用来添加/删除 委托函数

- 事件的执行:只能在定义事件的类内部执行
// 声明委托类型
public delegate void MyDelegate(int a, int b); class Person
{
// 定义事件
public event MyDelegate myEvent; public void FireEvent(int a, int b)
{
if (myEvent != null)
myEvent(a, b); // 必须在定义事件的类内部执行
}
} public void PrintAddNum(int a, int b)
{
Debug.Log("PrintAddNum:" + (a + b));
}
public void PrintMultNum(int a, int b)
{
Debug.Log("PrintMultNum:" + (a * b));
} void Start()
{
Person person = new Person();
person.myEvent += PrintAddNum;
person.myEvent += PrintMultNum; //person.myEvent(2, 5); // 报错,无法在定义事件的类外部执行
person.FireEvent(, ); // 正确
}- 注:事件不能在类的外部使用赋值“=”,但是在类内部可以重新赋值“=”
- 事件与委托一样,也支持多播:可以通过“+=”、“-=” 来添加事件、删除事件
- 事件访问器(add / remove)
- 事件与委托的关系,与 字段与属性的关系是一样的,所以也有访问器
- 关键字: add remove
public event MyDelegate myEvent
{
add
{
... //执行 += 运算符的代码
}
remove
{
... //执行 -= 运算符的代码
}
}
- 为什么使用事件?
- 对委托的封装
- 对外只能通过事件来 添加/删除 绑定的委托函数
- 事件只能在定义事件的 类内部执行
- 满足观察者模式(发布者-订阅者)
- 当然不使用事件,也可以实现对委托的封装
- 将委托定义为private,然后对外提供添加/删除委托函数的方法(相当于手动实现事件中的add_xxx和remove_xxx)
// 声明委托类型
public delegate void MyDelegate(int a, int b); class Person
{
// 定义一个私有委托变量
public MyDelegate myDelegate;
// 添加
public void AddEvent(MyDelegate d)
{
myDelegate += d;
}
// 删除
public void RemoveEvent(MyDelegate d)
{
myDelegate -= d;
}
// 执行
public void FireEvent(int a, int b)
{
if (myDelegate != null)
myDelegate(a, b); // 必须在定义事件的类内部执行
}
} public void PrintAddNum(int a, int b)
{
Debug.Log("PrintAddNum:" + (a + b));
}
public void PrintMultNum(int a, int b)
{
Debug.Log("PrintMultNum:" + (a * b));
} void Start()
{
Person person = new Person(); person.AddEvent(PrintAddNum); // this.PrintAddNum
person.RemoveEvent(PrintMultNum); // this.PrintMultNum person.FireEvent(, );
}
- 对委托的封装
【Unity|C#】基础篇(8)——委托(Delegate)/ 事件(Event)的更多相关文章
- C#基础篇 - 理解委托和事件
1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针.简单理解,委托是一种可以把函数当做参数传递的类型.很多情况下,某 ...
- C# 篇基础知识5——委托和事件
事件处理程序是基于“委托”机制运行的. 1.委托 (1)委托的定义和使用 有时需要将一个函数作为另一个函数的参数,这时就要用到委托(Delegate)机制.例如设计一个马戏表演函数: //定义委托 d ...
- 秒杀多线程第六篇 经典线程同步 事件Event
原文地址:http://blog.csdn.net/morewindows/article/details/7445233 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权” ...
- 转--- 秒杀多线程第六篇 经典线程同步 事件Event
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...
- js 基础篇(点击事件轮播图的实现)
轮播图在以后的应用中还是比较常见的,不需要多少行代码就能实现.但是在只掌握了js基础知识的情况下,怎么来用较少的而且逻辑又简单的方法来实现呢?下面来分析下几种不同的做法: 1.利用位移的方法来实现 首 ...
- (spring-第15回【IoC基础篇】)容器事件
五个人在报社订阅了报纸.报社一旦有了新报纸,就派员工分别送到这五个人手里.在这个例子中,“报纸”就是事件,“报社”就是广播器,五个订阅者就是监听器.广播器收到事件,把事件传给监听器,监听器对事件做一些 ...
- 自动化测试基础篇--Selenium鼠标键盘事件
摘自https://www.cnblogs.com/sanzangTst/p/7477382.html 前面几篇文章我们学习了怎么定位元素,同时通过实例也展示了怎么切换到iframe,怎么输入用户名和 ...
- Unity 3D观察者设计模式-C#委托和事件的运用
C#观察者设计模式 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心创新! ...
- cocos2dx基础篇(7) 触碰事件
cocos2dx游戏引擎的重点是在于移动设备的跨平台开发,而移动设备上的游戏大部分都是通过屏幕触碰来进行的.比如主菜单的按钮触碰,打飞机中飞机的触碰移动,都需要用到触碰操作.想一想之前讲的菜单按钮CC ...
随机推荐
- Dart中类的getter和setter
Dart类Getters和Setter Getters和Setter(也称为访问器和更改器)允许程序分别初始化和检索类字段的值. 使用get关键字定义getter或访问器.Setter或存取器是使用s ...
- 页面上icon较多,又不想使用臃肿的结构怎么办?
[先看效果图] 例如这种排版,常规有两种情况 1.把[“ & ”]+ 白色背景切一起 2.写结构的时候复杂,例如:div>img*2 +文字标签 读到这里,可能有人说,第一种情况为什么两 ...
- 剑指offer-面试题35-复杂链表的复制-链表
/* 题目: 实现一个函数,复制复杂链表,返回复制链表的头节点. */ /* 思路: 第一步,复制一个链表S‘,插在原链表S中. 第二步,链表S’复制链表S的random指针. 第三步:拆分链表S和S ...
- spring中JdbcTemplate使用
1.maven依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- final关键字在JVM中的理解
我们先来看两段代码与运行结果,根据结果去分析解释一下 不加final关键字: package com.waibizi; public class demo02 { public static void ...
- 【Newtonsoft.Json】json序列化小驼峰格式(属性名首字母小写)
我是一名 ASP.NET 程序员,专注于 B/S 项目开发.累计文章阅读量超过一千万,我的博客主页地址:https://www.itsvse.com/blog_xzz.html 只需要设置JsonSe ...
- oracke数据库分区新增
1.发现一些过程执行报错,报错原因是'ora-14400插入的分区关键字未映射到任何分区',原来是2020年库表的时间子分区未扩展: 2.扩展子分区的语句是 ALTER TABLE 表名 MODIFY ...
- Linux_Centos7安装VNC实现远程桌面
一.首先安装GNOME桌面 yum groupinstall -y "GNOME Desktop"# 安装完成后,修改默认启动方式为图形化界面systemctl set-defau ...
- Luogu P1993 题解
p1993 小康的农场 CSP_S 1=之后就没怎么写题解.. 推荐博客食用 预备知识 明显这是一道差分约束的题,以下简称差分 有些人可能不了解差分,请点 [传送门] 至于用差分做的题的特征,无一都是 ...
- win10配置jdk环境变量
备注:这里以jdk-10.0.2_windows-x64_bin为例 1.jdk安装目录如下 2.右键“此电脑”-点击“属性”-点击“高级系统设置”-点击“环境变量”-选中“Path”-点击“编辑” ...