一:委托
? ? ? ? 所有的代碼語言創(chuàng)造者母語都是英語,我們從英語翻譯到中文的過程中難免會(huì)存在一些不太能還原本意的詞,比如我之前一直不理解構(gòu)造函數(shù)和析構(gòu)函數(shù),只知道這倆貨作用相反,直到我看到了它的英文意思,Construstor/Distructor,我才徹底理解了他們的作用。
? ? ? ? 接下來我們來看委托,Delegate,來看兩個(gè)例句,深入理解Delegate...
Can?you?delegate?some?tasks?or?projects?????????????????????????????????你能夠分配一些任務(wù)或者項(xiàng)目嗎?
So?why?not delegate?more?work?to your?employees?? ??所以你為啥不給你的員工多分分配點(diǎn)任務(wù)?
從上面的句子中我們可以看到,他就是分配,也就是委托的意思(但是感覺可能有些人對(duì)委托的理解不如分配來的直接,至少對(duì)我來說是這樣)
微軟官方的解釋是委托可以獲取一個(gè)或多個(gè)方法,但是類型和返回值必須和方法相同,可以理解成委托是方法的抽象,也就是說定義一個(gè)方法的模板,至于這個(gè)方法具體是怎么樣的,就由方法自己去實(shí)現(xiàn)。這點(diǎn)和函數(shù)指針很像...后續(xù)寫單播委托時(shí)候再添加這部分內(nèi)容。
我們可以說,委托就是一個(gè)方法的類型
話不多說,看代碼...
一、單播委托——一次只能裝進(jìn)去一個(gè)方法
Public delegate 返回值 MyDelegate(參數(shù)1,參數(shù)2)
就這么一步,我們就把委托定義出來了,接下來要做的就是把這玩意兒實(shí)例化出來,那我們?cè)趺窗盐覀兊姆椒ńo委托呢?
第一點(diǎn)就是我們需要有和定義的委托類型一致的返回值和參數(shù)列表
其實(shí)委托就是起源于C語言的函數(shù)指針,不過在C#以委托的形式存在了
但是在Java中沒有委托這么一說...
//使用Typedef將該函數(shù)指針聲明為一種類型,它是指向兩個(gè)參數(shù)為int,返回值為int的函數(shù)指針
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;
}
//函數(shù)指針的使用
Calculator Pointer1 = &Add;
Calculator Pointer2 = &Multiply;
//這樣我們?cè)谡{(diào)用函數(shù)的時(shí)候就不再寫函數(shù),而是采用函數(shù)指針的方法,間接的指向了該類型的函數(shù)
Pointer1(0,1);
Pointer2(1,2);
從上面的函數(shù)指針我們可以看出,我們?cè)谧?cè)方法的時(shí)候,可以間接的聲明一個(gè)和該方法類型和返回值都一致的指針類型,從而調(diào)用函數(shù)指針即可,那么我們的委托和它是類似的....
接下來看我們的委托的實(shí)例化和方法的插入,可以使用new,也可以直接引用方法:
//實(shí)例化委托
MyDelegate myDelegate = new MyDelegate(Function);
//簡(jiǎn)化寫法
myDelegate = Telegate;
返回值類型 Function(參數(shù)1,參數(shù)2)
{
方法體;
}
委托的調(diào)用,可以使用Invoke,也可以直接寫委托名+()
可以通過Invoke進(jìn)行調(diào)用委托
myDelegate.Invoke();
也可以直接myDelegate();
二、多播委托——一次裝多個(gè)方法,但不安全
在上面的方法添加環(huán)節(jié),我們只需要做小小的修改
myDelegate += ChangeColor;
myDelegate += Log;
但是其實(shí)我們沒事兒也不會(huì)這么干,這樣長(zhǎng)期下來有可能會(huì)存在內(nèi)存泄漏,如果順序執(zhí)行列表中方法有一個(gè)出錯(cuò)了,后面的就都不會(huì)執(zhí)行了,所以我們還有其他更好的選擇
三、Action委托和Func委托
大多數(shù)的情況下,我們不太需要自己去聲明委托,而是使用現(xiàn)成的委托即可,Unity為我們內(nèi)置了兩種泛型委托
1)Action委托——返回值必須為空,參數(shù)可有可無
//聲明無參數(shù)的Action委托
Action action;
//聲明有參數(shù)的Action委托
Action<string,float> action1;
//Action的使用
action = new Action(同參數(shù)的方法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委托——返回值必須有,但是參數(shù)可有可無
//聲明Func委托,前面是參數(shù),后面是返回值
Func<double,double,double> func1;
//使用
func1 = new Func<double,double,double>(Add);
//跟Func結(jié)構(gòu)一樣的方法
public double Add(doublea ,double b)
{
return a+b;
}
二:事件
事件,使對(duì)象或類具備通知能力
事件,是委托字段的一個(gè)包裝器,它對(duì)于委托字段的訪問起了限制作用
對(duì)外界隱藏了委托實(shí)例的大部分功能,僅暴露添加/移除事件處理器的功能
日常開發(fā)中,自己聲明事件的機(jī)會(huì)比較少,一般是用已有事件比較多...
java中沒有委托,事件這么一說,只用Interface來實(shí)現(xiàn)
一、事件的五個(gè)重要因素
事件的擁有者
事件成員(Event)
事件的響應(yīng)者(Event Subscriber)
事件處理器(Event handler,本質(zhì)上是回調(diào)方法)
事件訂閱(+=)
他們之間的關(guān)系可以如下幾種:
1:事件的擁有者類和事件的響應(yīng)者是不同的類
using System.Timers;
using UnityEngine;
public class EventTimothyLiu1 : MonoBehaviour
{
private void Start()
{
//世間間隔,每過1s就觸發(fā)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類,事件的響應(yīng)者是自定義的Boy,Girl類
所以事件的響應(yīng)者和擁有者是兩個(gè)類
第二個(gè)例子:
using System;
using System.Windows.Forms;
namespace EventLiu
{
class Program
{
static void Main(string[] args)
{
//事件的擁有者:Form
Form form = new Form();
//事件的響應(yīng)者:Controller
Controller controller = new Controller(form);
form.ShowDialog();
}
}
class Controller
{
private Form form;
/// <summary>
/// CTOR再加Tab即可編寫出構(gòu)造器
/// 也叫構(gòu)造函數(shù),每個(gè)類都必須有
/// 在構(gòu)建類的引用時(shí)自動(dòng)運(yùn)行的方法!
/// </summary>
public Controller(Form form)
{
if (form!= null)
{
//this就是類的實(shí)例
//this后是我們定義的字段,后面是參數(shù)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,事件的響應(yīng)者是Controller
2:事件的擁有者同時(shí)也是事件的響應(yīng)者
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
事件的響應(yīng)者也是myForm的實(shí)例
3:事件的擁有者是事件的響應(yīng)者的成員(頻率最高)
事件的響應(yīng)者用自己的方法訂閱者自己的字段成員的事件
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是事件
//事件的響應(yīng)者是this,也就是MyForm的實(shí)例對(duì)象
this.button.Click += this.ButtonClicked;
}
//事件處理器
private void ButtonClicked(object sender, EventArgs e)
{
this.textBox.Text = "Hello";
}
}
}
在該段代碼中,我們自己創(chuàng)建MyForm類,繼承自Form
事件的擁有者是該類中的成員Button,事件的響應(yīng)者是該類的實(shí)例化
也就是兒子有事件,爸爸訂閱了。? ? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
4:事件的響應(yīng)者是事件的擁有者的成員
二、事件的完整聲明格式
用以下代碼來看:
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,習(xí)慣
/// </summary>
public class OrderEventArgs1:EventArgs
{
public string DishName { get; set; }
public string size { get; set; }
}
//把委托放置在類外
/// <summary>
/// 用EvenetHandler原因:
/// 1:別人看到這個(gè)后綴就知道這個(gè)委托是專門用來聲明事件的
/// 2:EventHandle表明委托來約束事件處理器
/// 3:委托的實(shí)例是用來存儲(chǔ)事件處理器
/// </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是事件的接受者,它擁有事件的響應(yīng)器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進(jìn)入餐廳,走進(jìn),坐下,思考,然后觸發(fā)點(diǎn)餐事件
Customer是點(diǎn)餐事件的擁有者
Waiter是點(diǎn)餐事件的接受者,它還得有點(diǎn)餐的響應(yīng)事件,就是算出價(jià)格,并采取行動(dòng)
在自定義點(diǎn)餐事件的時(shí)候,我們采用了EventHandler委托來給事件做支撐
創(chuàng)建了EventArgs類來承載事件,誰發(fā)起的事件,誰就是委托的第一個(gè)參數(shù)
但是這樣寫比較復(fù)雜,我們來看事件的簡(jiǎn)略聲明格式
三、事件的簡(jiǎn)單聲明格式
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>
/// 先聲明委托,委托兩個(gè)參數(shù)分別是事件的擁有者和事件需要使用的參數(shù)
/// </summary>
/// <param name="customer2"></param>
/// <param name="e"></param>
public delegate void OrderEventHandler2(Customer2 customer2, OrderEventArgs2 e);
/// <summary>
/// 容納事件參數(shù)
/// </summary>
public class OrderEventArgs2:EventArgs
{
public string DishName { get; set; }
public string size { get; set; }
}
/// <summary>
/// 事件的擁有者
/// </summary>
public class Customer2
{
//原本需要先實(shí)例化委托,然后將委托賦予事件中進(jìn)行使用
//private OrderEventHandler1 OrderEventHandler1;
//public event OrderEventHandler1 Order
//{
// add { this.OrderEventHandler1 += value; }
// remove { this.OrderEventHandler1 -= value; }
//}
//現(xiàn)在進(jìn)行簡(jiǎn)化
/// <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;
}
}
還是上述的代碼,我們進(jìn)行了簡(jiǎn)化
我們將如下代碼:
private OrderEventHandler1 OrderEventHandler1;
public event OrderEventHandler1 Order
{
add { this.OrderEventHandler1 += value; }
remove { this.OrderEventHandler1 -= value; }
}
簡(jiǎn)化成了:
public event OrderEventHandler2 Order2
這樣的簡(jiǎn)化是微軟后臺(tái)進(jìn)行了,我們可以直接如此書寫
其實(shí)并非不存在委托字段的聲明,還是存在的,只是存在于后臺(tái)中,微軟后臺(tái)聲明了,我們不需要看到,所以可以進(jìn)行簡(jiǎn)化
還可以進(jìn)一步簡(jiǎn)化:
將聲明委托的部分省略掉,即使用微軟定義好的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類中的實(shí)例化Cutsome3和OrderEventArgs3的實(shí)例化使用
采用實(shí)例化,參數(shù) as 的方法,將參數(shù)轉(zhuǎn)化為我們需要的
為什么有了委托字段/屬性,還需要事件?文章來源:http://www.zghlxwxcb.cn/news/detail-737586.html
為了程序邏輯更加有道理,更加安全,謹(jǐn)防“借刀殺人”文章來源地址http://www.zghlxwxcb.cn/news/detail-737586.html
三:UnityEvent/UnityAction
到了這里,關(guān)于Unity/C#------委托與事件(一篇文章徹底搞懂...)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!