C#中委托、事件和回调的使用及说明
作者:云无极
委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
事件是对委托的封装。如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用到新的委托对象,也就删除了当前要调用的方法列表;
定义一个事件有两步,首先定义一个委托,它包括了这件事的“协议”和委托方法(由谁去做);其次,用event关键字和相关委托声明这个事件。事件像是一个接口,封装了委托所定的“协议”。由于委托已经定义了协议,剩下的就是按这个协议去办事,至于怎么做它并不关心。调用者无法访问委托对象。
回调函数就是把一个方法的传给另外一个方法去执行。回调函数只是一个功能片段,由用户按照回调函数的调用约定来实现的一个函数。可以把任意一个符合这个委托的方法传递进去,意思就是说这部分代码是可变的。而设计上有一个抽离出可变部分代码的原则,这种用法无疑可以用到那种场合了。从上可知,事件和回调都是对委托的一种用法。事件是把委托封装起来,而回调函数则是由委托绑定不同的函数来实现不同的功能。
委托的使用案例
定义委托和方法
//委托定义(要与方法中参数一致) public delegate int DelegateTest(int n1, int n2); class Math { //方法定义(委托要执行的方法,本案例把方案写到class类中) public int Multiply(int n1, int n2) { return n1 * n2; } public int AddTest(int n1, int n2) { return n1 + n2; } }
委托的使用
private void button1_Click(object sender, EventArgs e) { Math objMath = new Math(); //创建委托对象 DelegateTest delegateDemo1; //将方法与委托对象关联起来 (委托:将方法当作另一个方法的参数来进行传递) //delegateDemo1 = new CallDelegate(objMath.Multiply); delegateDemo1 = objMath.Multiply; //与上面方法相同 //delegateDemo1 += objMath.AddTest; //给委托对象再绑定一个方法,若该条代码执行,显示结果为17 //将委托实例化 int result = delegateDemo1(5, 12); richTextBox1.AppendText(result.ToString() + "\r"); //****委托的另外一写法,通过Action或Func,如果有返回值用Func,否则用Action *****// //Func<int, int, int> func1 = new Func<int, int, int>(objMath.Multiply); Func<int, int, int> func1 = objMath.Multiply; richTextBox1.AppendText(func1.Invoke(6, 13) + "\r"); }
执行的结果
利用Action或Func简化代码
private void button4_Click(object sender, EventArgs e) { Math objMath = new Math(); //委托的另外一写法,通过Action或Func,如果有返回值用Func,否则用Action //Func<int, int, int> func1 = new Func<int, int, int>(objMath.Multiply); //同下 Func<int, int, int> func1 = objMath.Multiply; richTextBox1.AppendText(func1.Invoke(6, 13) + "\r"); }
事件的使用案例
class ClassA { public string ClassAinfo = "A 默认!"; public void DispInfo() { ClassAinfo = "A 收到!"; } } class ClassB { public string ClassAinfo = "B 默认!"; public void DispInfo() { ClassAinfo = "B 收到!"; } } class DelegatEventTest { //定义委托 public delegate void MyDelegateEventHandler(); //定义事件 public event MyDelegateEventHandler NotifyEveryOne; //调用事件 public void Notify() { if (NotifyEveryOne != null) { NotifyEveryOne(); } } }
事件的使用
private void button3_Click(object sender, EventArgs e) { //创建classA和classB的对象 ClassA objA = new ClassA(); ClassB objB = new ClassB(); //创建委托的对象 DelegatEventTest event1 = new DelegatEventTest(); //订阅事件(类似于 方法与委托事件的关联) event1.NotifyEveryOne += new DelegatEventTest.MyDelegateEventHandler(objA.DispInfo); //event1.NotifyEveryOne += new DelegatEventTest.MyDelegateEventHandler(objB.DispInfo); event1.Notify(); richTextBox1.AppendText(objA.ClassAinfo + "\r"); richTextBox1.AppendText(objB.ClassAinfo + "\r"); }
带参数的事件案例
参考网上的案例,场景:首领boyK要搞一场鸿门宴,吩咐部下boyA和boyB各自带队埋伏在屏风两侧,约定以杯为令:若左手举杯,则boyA带队杀出;若右手举杯,则boyB带队杀出;若直接摔杯,则boyA和boyB同时杀出。boyA和boyB袭击的具体方法,首领boyK并不关心。
boyK的定义
public class BoyK { //定义委托 public delegate void RaiseEventHandler(string hand); public delegate void FallEventHandler(); //定义事件 public event RaiseEventHandler RaiseEvent; public event FallEventHandler FallEvent; //调用事件(例:举手事件) public void Raise(string hand) { if (RaiseEvent!=null) { RaiseEvent(hand); } } //调用事件(例:摔杯事件) public void Fall() { if (FallEvent!=null) { FallEvent(); } } }
boyA的定义
class BoyA { public string str = "A待命"; BoyK boyk; public BoyA(BoyK k) { boyk = k; k.RaiseEvent += new BoyK.RaiseEventHandler(k_RaiseEvent); //订阅举杯事件 k.FallEvent += new BoyK.FallEventHandler(k_FallEvent); //订阅摔杯事件 } public void Attack() { str = "A开始**"; } //boyK举杯的动作 void k_RaiseEvent(string hand) { if (hand.Equals("左")) { Attack(); } } void k_FallEvent() { Attack(); } }
boyB的定义
class BoyB { public string str = "B待命"; BoyK boyk; public BoyB(BoyK k) { boyk = k; k.RaiseEvent += new BoyK.RaiseEventHandler(k_RaiseEvent); //订阅举杯事件 k.FallEvent += new BoyK.FallEventHandler(k_FallEvent); //订阅摔杯事件 } public void Attack() { str = "B开始**"; } void k_RaiseEvent(string hand) { if (hand.Equals("右")) { Attack(); } } void k_FallEvent() { Attack(); } }
事件的使用
private void button2_Click(object sender, EventArgs e) { BoyK boyK = new BoyK(); BoyA boyA = new BoyA(boyK); BoyB boyB = new BoyB(boyK); //boyK.Raise("左"); boyK.Raise("右"); //boyK.Fall(); richTextBox1.AppendText(boyA.str+"\r"); richTextBox1.AppendText(boyB.str + "\r"); }
运行效果
回调函数的使用案例
实际开发中,下面这个类会封装起来,只提供函数接口。相当于系统底层
//实际开发中,下面这个类会封装起来,只提供函数接口。相当于系统底层 class CalculateClass { public delegate int SomeCalculateWay(int num1, int num2); //将传入参数在系统底层进行某种处理,具体计算方法由开发者开发,函数仅提供执行计算方法后的返回值 //下面的代码中相当于调用了一个回调函数 public int Calculate(int num1, int num2, SomeCalculateWay call) { return call(num1, num2); } }
开发层处理,开发人员编写具体的计算方法
//开发层处理,开发人员编写具体的计算方法 class FunctionClass { public int GetSum(int a, int b) { return a + b; } public int GetMulti(int a, int b) { return a * b; } }
用户层,执行输入等操作
private void button4_Click(object sender, EventArgs e) { CalculateClass cc = new CalculateClass(); FunctionClass fc = new FunctionClass(); int result1 = cc.Calculate(2, 3, fc.GetSum); int result2 = cc.Calculate(2, 3, fc.GetMulti); richTextBox1.AppendText(result1 + "\r"); richTextBox1.AppendText(result2 + "\r"); }
说明:上述代码中的FunctionClass中的GetSum()和GetMulti()两个函数称为回调函数。
可以看到整个程序中并没有哪个地方通过类似GetSum(1,2)这种形式调用了该函数,只有将其当作另一个函数的参数来进行调用。
如cc.PrintAndCalculate(2, 3, fc.GetSum)。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。