第5章:事件与委托
本章目标
掌握委托的使用
掌握事件的使用
本章内容 什么是委托 委托是一种存储函数引用的类型,就像我们定义一个string str一样,这个str变量就是string类型。因为C#中没有函数类型,但是可以定义一个委托类型,把一个函数赋给这个委托,类似于C++中的函数指针 。
委托的定义与类的定义类似,先定义,再声明,再创建实例,再使用,定义时需要加上delegate关键字但是不需要函数体 。
与委托关联可以是任何类或者结构中的方法,可以是静态方法,只要是可以访问的方法都可以。创建一个委托类型使用关键字delegate(委托)
自定义委托
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate void MyDelegate (string s ) ; internal class Program { static void Main (string [] args ) { MyDelegate d1 = Test1; MyDelegate d2 = Test2; MyDelegate d3 = new MyDelegate(Test3); d1("hello" ); d2("hello" ); d3("hello" ); Console.ReadKey(); } static void Test1 (string s ) { Console.WriteLine("调用了Test1()方法,参数:" + s); } static void Test2 (string s ) { Console.WriteLine("调用了Test2()方法,参数:" + s); } static void Test3 (string s ) { Console.WriteLine("调用了Test3()方法,参数:" + s); } } }
Action委托 Action是系统内置的委托类型,这样就不需要通过delegate定义委托类型,可以直接使用Action作为类型 Action只能指向一个没有返回值的方法
1 2 3 4 5 6 7 8 9 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 42 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { internal class Program { static void Main (string [] args ) { Action action1 = Fun1; Action<int > action2 = Fun2; Action<int , string > action3 = Fun3; action1(); action2(15 ); action3(10 , "hello" ); Console.ReadKey(); } static void Fun1 () { Console.WriteLine("无参" ); } static void Fun2 (int num ) { Console.WriteLine($"num:{num} " ); } static void Fun3 (int num, string str ) { Console.WriteLine($"num:{num} ,str:{str} " ); } } }
Func委托 Func是系统内置的委托类型,这样就不需要通过delegate定义委托类型,可以直接使用Func作为类型 Func只能指向有返回值的方法,最后一个泛型参数是返回值类型 。
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 47 48 49 50 51 52 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { internal class Program { static void Main (string [] args ) { Func<int > action1 = Fun1; Func<int ,int > action2 = Fun2; Func<int , string ,int > action3 = Fun3; Func<int , double , char , string > action4=Fun4; action1(); action2(15 ); action3(10 , "hello" ); action4(10 , 3.14 ,'男' ); Console.ReadKey(); } static int Fun1 () { Console.WriteLine("无参" ); return 10 ; } static int Fun2 (int num ) { Console.WriteLine($"num:{num} " ); return num*2 ; } static int Fun3 (int num, string str ) { Console.WriteLine($"num:{num} ,str:{str} " ); return num * 3 ; } static string Fun4 (int a, double b, char c ) { Console.WriteLine($"a:{a} ,b:{b} ,c:{c} " ); return "hello" ; } } }
多播委托 多播委托是指一个委托指向多个方法,使用+=和-=去添加或移除委托,其实是调用了Delegate.Combine和Delegate.Remove方法,他会按照委托添加的顺序依次调用方法。对于有返回值的委托多播委托只能得到最后一个方法的返回结果
1 2 3 4 5 6 7 8 9 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 42 43 44 45 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate void MyDelegate (string s ) ; internal class Program { static void Main (string [] args ) { MyDelegate d1 = Test1; d1 += Test2; d1 += Test3; d1("hello" ); Console.ReadKey(); } static void Test1 (string s ) { Console.WriteLine("调用了Test1()方法,参数:" + s); } static void Test2 (string s ) { Console.WriteLine("调用了Test2()方法,参数:" + s); } static void Test3 (string s ) { Console.WriteLine("调用了Test3()方法,参数:" + s); } } }
使用匿名方法 匿名方法就是没有名称没有返回值的方法,任何使用委托变量的地方都可以使用匿名方法,但是因为没有名字所以不能自己调用自己而只能通过委托去调用
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate void MyDelegate (string s ) ; internal class Program { static void Main (string [] args ) { Action de1 = delegate { Console.WriteLine("de1" ); }; Action<int > de2 = delegate (int num) { Console.WriteLine("de2" ); }; Action<int , int > de3 = delegate (int num1, int num2) { Console.WriteLine("de3" ); }; Func<int > de4 = delegate { Console.WriteLine("de4" ); return 11 ; }; MyDelegate de5= delegate (string s) { Console.WriteLine("de5" ); }; de1(); de2(5 ); de3(5 ,10 ); de4(); de5("hello" ); Console.ReadKey(); } } }
使用Lambda表达式 lambda表达式就是匿名方法的简写形式
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 47 48 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate void MyDelegate (string s ) ; internal class Program { static void Main (string [] args ) { Action de1 = ()=> Console.WriteLine("de1" ); Action<int > de2 = (int num) => Console.WriteLine("de2" ); Action<int , int > de3 = (int num1, int num2) => Console.WriteLine("de3" ); Func<int > de4 = ()=> { Console.WriteLine("de4" ); return 11 ; }; MyDelegate de5= delegate (string s) { Console.WriteLine("de5" ); }; de1(); de2(5 ); de3(5 ,10 ); de4(); de5("hello" ); Console.ReadKey(); } } }
泛型委托 1 2 3 4 5 6 7 8 9 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 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate int DelCompare <T >(T t1, T t2 ) ; class Program { static void Main (string [] args ) { DelCompare<int > de = Compare1; Console.WriteLine(de(35 , 24 )); Console.ReadKey(); } public static int Compare1 (int n1, int n2 ) { return n1 - n2; } } }
利用委托作为参数:
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { public delegate int DelCompare <T >(T t1, T t2 ) ; class Program { static void Main (string [] args ) { int [] nums = { 1 , 2 , 3 , 4 , 5 }; int max = GetMax<int >(nums, Compare1); Console.WriteLine(max); string [] names = { "abcdefg" , "fdsfds" , "fdsfdsfdsfdsfdsfdsfdsfsd" , "sss" }; string max1 = GetMax<string >(names, (string s1, string s2) => { return s1.Length - s2.Length; }); Console.WriteLine(max1); Console.ReadKey(); } public static T GetMax <T >(T[] nums, DelCompare<T> del ) { T max = nums[0 ]; for (int i = 0 ; i < nums.Length; i++) { if (del(max, nums[i]) < 0 ) { max = nums[i]; } } return max; } public static int Compare1 (int n1, int n2 ) { return n1 - n2; } } }
事件 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。
事件可以称为是一种特殊签名的委托 需要注意的是,委托可以在任何类中调用而事件只能在类的内部调用
1 2 3 4 5 6 7 8 9 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace CH05Demo { class Program { static void Main (string [] args ) { Student stu = new Student(); stu.OnAgeCheck += Age_Check; stu.Age = 130 ; Console.WriteLine("年龄:" + stu.Age); Console.ReadKey(); } public static void Age_Check () { Console.WriteLine("年龄不满足要求" ); } } class Student { public delegate void AgeCheckHandler () ; public event AgeCheckHandler OnAgeCheck; private int age; public int Age { get { return this .age; } set { if (value > 0 && value < 100 ) { this .age = value ; } else { if (this .OnAgeCheck != null ) { this .OnAgeCheck(); } } } } } }
本章总结 委托与事件的区别:
委托是修饰的是一个类型,事件修饰的是一个对象
委托可以作为方法参数传递,事件不能
委托在赋值时无论类内还是类外都可以用=或+=赋值,事件在类内可以使用=,在类外只能通过+=赋值
委托可以在类内和类外调用,事件只能在类内调用
委托用于回调函数、方法引用参数的传递,事件用于观察者模式、消息订阅等
课后作业 1.自定义委托,包含两个string类型的参数, 返回类型为string,要求实现以下功能: 比较字符串的长短,返回较长的那个字符串. (1)常规方法实现 (2)匿名方法实现 (3)lambda表达式实现
2.使用Func委托实现以下功能: (1)无参,返回结果为学号(string类型,模板为”GCKJxxxx”)(lambda实现) (2)包含参数:最小值,最大值,返回结果:指定范围的随机数(lambda实现)
3.自定义泛型委托,包含两个T类型的参数,返回类型为T,要求实现以下功能: 比较大小, 如果为数字,则直接比较, 如果为字符串,则比较其hashcode 如果为日期,则直接比较 lambda表达式实现
4.定义泛型委托实现排序,参数为T[],返回类型为void,要求实现以下功能(升序): (1)对一组整数进行排序 (2)对一组字符串进行排序(比较hashcode) (3)对一组学生对象进行排序(学生类包含:姓名、年龄、性别,根据学生的年龄排序)
5.定义泛型委托实现比较,参数1为T1,参数2为T2,返回类型为int; 定义泛型方法实现排序,参数1为T[] ,参数2为委托对象,返回类型为void,要求实现以下功能。(升序): (1)对一组整数进行排序 (2)对一组字符串进行排序(比较hashcode) (3)对一组学生对象进行排序(学生类包含:姓名、年龄、性别,根据学生的年龄排序)
6.在学生类中定义姓名赋值错误事件OnNameError,年龄赋值错误事件OnAgeError,性别赋值错误事件OnSexError. 要求在学生相关属性赋值错误时,触发对应事件。