委托事件
一. 什么是委托
通俗的讲,就是一个能存放符合某种格式(方法签名)的方法的指针的容器。
二.委托入门
程序示例:
1 //声明委托类(必须指定返回值类型和方法参数列表) 2 public delegate void DGSayHi(string str); 3 4 public partial class Form1 : Form 5 { 6 public Form1() 7 { 8 InitializeComponent(); 9 } 10 11 private void button1_Click(object sender, EventArgs e) 12 { 13 //创建委托对象,并为委托对象添加一个方法指针(方法对象地址) 14 DGSayHi dgSayHi = new DGSayHi(SayHiSomeWhere); 15 //调用委托(委托内部的方法就会被调用) 16 //注意:不能存放 签名 和 委托签名 不一致的方法 17 dgSayHi("霍啊啊"); 18 } 19 20 void SayHiInChina() 21 { 22 MessageBox.Show("你好"); 23 } 24 25 void SayHiSomeWhere(string location) 26 { 27 MessageBox.Show(location+"你好"); 28 } 29 30 }
三.委托源码
1. 语法糖:在C#中有很多简洁语法,实质是由编译器在编译时转成完整语法,那么这种简洁语法叫做语法糖。
2. 委托的两个目的:
能将方法作为参数和返回值;
调用一个委托,执行N个方法(多播委托);
3. 代码示例:
1 //1. 声明委托的本质: 2 //委托编译后生成一个同名类(DGTest) 3 //继承关系:DGTest->MulticastDelegate->Delegate 4 public delegate void DGTest(); 5 public partial class Form1 : Form 6 { 7 public Form1() 8 { 9 InitializeComponent(); 10 } 11 } 12 private void btnTest_Click(object sender, EventArgs e) 13 { 14 //语法糖:在C#中有很多 简洁语法,实质是由编译器 在编译时 转成 完整语法,那么这种 简洁语法叫做语法糖 15 //2.创建委托对象 16 DGTest dg = Test;//new DGTest(this.Test); 17 /*3.为委托追加方法的本质: 18 * 是为 被追加的方法创建一个新的委托对象,并将 方法 指针存入对象的 父类的父类(Delegate)的 IntPtr 变量中 19 * 然后再将 新创建的委托 添加到 当前委托对象(dg)的 数组中 20 */ 21 dg += Test2;//编译后:(DGTest) Delegate.Combine(dg, new DGTest(this.Test2)); 22 dg += Test3;//编译后:(DGTest) Delegate.Combine(dg, new DGTest(this.Test3)); 23 dg -= Test3;//编译后:(DGTest) Delegate.Remove(dg, new DGTest(this.Test3)); 24 /*4.调用委托,其实就是通过调用委托对象里的Invoke方法 遍历 委托内部的数组,然后依次调用 数组中的 方法 25 */ 26 dg();//编译后:dg.Invoke(); 27 28 /* 委托的作用: 29 * 1.将方法做为参数 30 * 2.将方法作为返回值 31 */ 32 }
四.委托当参数用
代码示例:
1 void Test1() 2 { } 3 void Test2() 4 { } 5 void Test3() 6 { } 7 public void InvokeTest(DGTest dgTest) 8 { 9 dgTest(); 10 } 11 //委托当参数用 12 private void btnPara_Click(object sender, EventArgs e) 13 { 14 InvokeTest(Test1); 15 InvokeTest(Test2); 16 InvokeTest(Test3); 17 }
五.委托当返回值
代码示例:
1 public DGTest InvokeTest(string strType) 2 { 3 switch (strType) 4 { 5 case "1": 6 return Test1; 7 case "2": 8 return Test2; 9 default: 10 return Test3; 11 } 12 } 13 //委托当返回值 14 private void btnReturn_Click(object sender, EventArgs e) 15 { 16 DGTest dg = InvokeTest("1"); 17 dg(); 18 }
六.自定义Each方法
程序示例:
1 //自定义Each方法 2 private void btnEach_Click(object sender, EventArgs e) 3 { 4 ArrayList list = new ArrayList(); 5 list.Add("你"); 6 list.Add("好"); 7 list.Add("啊"); 8 list.Add("!"); 9 Each(list,ShowName); 10 } 11 void ShowName(int index, object item) 12 { 13 MessageBox.Show(index + "item=" + item); 14 } 15 delegate void DGEachFunc(int index,object item); 16 public void Each(ArrayList list,DGEachFunc func) 17 { 18 for (int i = 0; i < list.Count; i++) 19 { 20 func(i, list[i]); 21 } 22 }
七.排序使用委托
代码示例:
1 #region 4.0 使用接口排序 2 /// <summary> 3 /// 使用接口排序 4 /// </summary> 5 /// <param name="sender"></param> 6 /// <param name="e"></param> 7 private void btnSort_Click(object sender, EventArgs e) 8 { 9 List<Dog> list = new List<Dog>(); 10 list.Add(new Dog() { name = "小白", age = 12 }); 11 list.Add(new Dog() { name = "小黑", age = 1 }); 12 list.Add(new Dog() { name = "小黄", age = 2 }); 13 14 for (int i = 0; i < list.Count; i++) 15 { 16 Console.WriteLine(i + ":" + list[i].name + ",age=" + list[i].age); 17 } 18 19 //此处 传递一个 IComparer接口的实现类 对象进去 ,目的 是 为 Sort 排序方法里的 比较过程 提供一个 比大小的方法。 20 list.Sort(new CompareDog()); 21 22 Console.WriteLine("排序后:"); 23 for (int i = 0; i < list.Count; i++) 24 { 25 Console.WriteLine(i + ":" + list[i].name + ",age=" + list[i].age); 26 } 27 } 28 #endregion 29 30 #region 5.0 使用委托排序 31 /// <summary> 32 /// 使用委托排序 33 /// </summary> 34 /// <param name="sender"></param> 35 /// <param name="e"></param> 36 private void btnSortDelegate_Click(object sender, EventArgs e) 37 { 38 List<Dog> list = new List<Dog>(); 39 list.Add(new Dog() { name = "小白", age = 12 }); 40 list.Add(new Dog() { name = "小黑", age = 1 }); 41 list.Add(new Dog() { name = "小黄", age = 2 }); 42 43 for (int i = 0; i < list.Count; i++) 44 { 45 Console.WriteLine(i + ":" + list[i].name + ",age=" + list[i].age); 46 } 47 48 //传入 的参数 为 一个 符合 Comparison 委托签名的方法 49 list.Sort(ComparisonDog); 50 51 //list.Sort((x, y) => x.age - y.age); 52 //int d = list.Max(x => x.age); 53 54 Console.WriteLine("排序后:"); 55 for (int i = 0; i < list.Count; i++) 56 { 57 Console.WriteLine(i + ":" + list[i].name + ",age=" + list[i].age); 58 } 59 } 60 61 int ComparisonDog(Dog x, Dog y) 62 { 63 return x.age - y.age; 64 } 65 #endregion
八.委托泛型案例-找最大值
代码示例:
1 #region 6.0 使用泛型方法 加 泛型委托 完成 通用版的 Max方法 2 /// <summary> 3 /// 使用泛型方法 加 泛型委托 完成 通用版的 Max方法 4 /// </summary> 5 /// <param name="sender"></param> 6 /// <param name="e"></param> 7 private void btnFindMax_Click(object sender, EventArgs e) 8 { 9 int[] arrInt = new int[5] { 1, 2, 5, 6, 3 }; 10 //int Max = MaxInt(arrInt); 11 int max = MaxValue<int>(arrInt, (x, y) => x - y); 12 13 14 string[] arrStr = new string[] { "a", "c", "b" }; 15 string maxLengthStr = MaxValue<string>(arrStr, (x, y) => x.Length - y.Length); 16 17 Dog[] dogs = new Dog[] { new Dog() { age = 1, name = "小白" }, new Dog() { age = 0, name = "小白2" }, new Dog() { age = 5, name = "小白3" } }; 18 Dog maxAgeDog = MaxValue<Dog>(dogs, (x, y) => x.age - y.age); 19 20 } 21 22 int MaxInt(int[] arr) 23 { 24 int maxInt = arr[0]; 25 for (int i = 1; i < arr.Length; i++) 26 { 27 if (maxInt < arr[i]) 28 { 29 maxInt = arr[i]; 30 } 31 } 32 return maxInt; 33 } 34 35 36 T MaxValue<T>(T[] arr, Comparison<T> func) 37 { 38 T maxInt = arr[0]; 39 for (int i = 1; i < arr.Length; i++) 40 { 41 //使用委托来 完成元素 大小的比较过程! 42 if (func(maxInt, arr[i]) < 0) 43 { 44 maxInt = arr[i]; 45 } 46 } 47 return maxInt; 48 } 49 #endregion
九.委托加工字符串(案例)
代码示例:
1 #region 9.0 调用 委托 加工 数组里的 每个字符串 2 /// <summary> 3 /// 调用 委托 加工 数组里的 每个字符串 4 /// </summary> 5 private void btnMakeUpStr_Click(object sender, EventArgs e) 6 { 7 //创建数组 8 string[] strArr = new string[] { "bb", "小李", "飞到" }; 9 //加工后产生新的数组,并将 方法 作为 第二个参数 传入 10 string[] strArrNew = MakeUpStrArr(strArr, MakeUpStr); 11 //遍历新数组 检查结果 12 for (int i = 0; i < strArrNew.Length; i++) 13 { 14 Console.WriteLine(strArrNew[i]); 15 } 16 } 17 18 //为传入字符串加一个 新的字符串 19 string MakeUpStr(string str) 20 { 21 return str += "- : )"; 22 } 23 24 //为传入的字符串数组 里的每个字符串 追加一个 新的字符串,并作为新字符串数组返回 25 string[] MakeUpStrArr(string[] arrStr, DGMakeUpStr dgMakeUpStr) 26 { 27 //创建新数组 28 string[] arrstrNew = new string[arrStr.Length]; 29 //遍历字符串数组 30 for (int i = 0; i < arrStr.Length; i++) 31 { 32 arrstrNew[i] = dgMakeUpStr(arrStr[i]);//此处调用 传入的委托对象里的方法 为 字符串加工 33 } 34 //返回新数组 35 return arrstrNew; 36 } 37 #endregion
十.委托找最大值(更加清晰的案例)
1 private void btnFindMaxByDel_Click(object sender, EventArgs e) 2 { 3 DGCompare dg = new DGCompare(IntCompare); 4 5 //找最大数值 6 object[] arrInt = {5, 4, 1, 9, 8}; 7 int maxInt = Convert.ToInt32(GetMax(arrInt, new DGCompare(IntCompare))); 8 9 //找最长字符串 10 object[] arrStr = { "ui", "xiaomi", "lei", "h" }; 11 string maxLenStr = GetMax(arrStr, StringCompare).ToString(); 12 13 //找年龄最大的狗 14 object[] arrDog = { new Dog { name = "小白", age = 21 }, new Dog { name = "小黑", age = 11 }, new Dog { name = "小花", age = 5 } }; 15 Dog oldestDog = GetMax(arrDog, DogCompare) as Dog; 16 } 17 18 /// <summary> 19 /// 根据 比较方法找最大值 20 /// </summary> 21 /// <returns></returns> 22 object GetMax(object[] values, DGCompare comparor) 23 { 24 object max = values[0]; 25 foreach (object obj in values) 26 { 27 if (comparor.Invoke(obj, max) > 0) 28 { 29 max = obj; 30 } 31 } 32 return max; 33 } 34 35 //--------------------------------- 三个不同的比较方法 ---------------------------- 36 public int IntCompare(object value1, object value2) 37 { 38 int i1 = (int)value1; 39 int i2 = (int)value2; 40 return i1 - i2; 41 } 42 43 public int StringCompare(object value1, object value2) 44 { 45 string i1 = (string)value1; 46 string i2 = (string)value2; 47 return i1.Length - i2.Length; 48 } 49 50 public int DogCompare(object value1, object value2) 51 { 52 Dog i1 = (Dog)value1; 53 Dog i2 = (Dog)value2; 54 return i1.age - i2.age; 55 }
十一.委托的封装(事件)
代码示例:
1 public partial class C03事件 : Form 2 { 3 //1.手动 创建 按钮对象 4 MyTripleButton btnMyTriple = new MyTripleButton(); 5 6 //2.手动 创建 改进型 按钮对象 7 MyTripleButtonSelf btnMyTriSelf = new MyTripleButtonSelf(); 8 9 //3.手动 创建 事件 按钮对象 10 MyTripleButtonSelfEvent btnMyTriSelfEvent = new MyTripleButtonSelfEvent(); 11 12 public C03事件() 13 { 14 InitializeComponent(); 15 16 //1.2.设置 按钮的 文本 和位置 17 btnMyTriple.Text = "自定义按钮"; 18 btnMyTriple.Location = new Point(100, 100); 19 //1.3.将按钮 显示到 窗体上 20 this.Controls.Add(btnMyTriple); 21 //1.4.为按钮 的 委托 设置一个 方法(按钮被点击的时候 会被 执行) 22 btnMyTriple.dgMyClick = MyTripleClick; 23 24 25 //2.2.设置 按钮的 文本 和位置 26 btnMyTriSelf.Text = "改进型 自定义按钮"; 27 btnMyTriSelf.Location = new Point(100, 150); 28 btnMyTriSelf.Size = new Size(150, 50); 29 //2.3.将按钮 显示到 窗体上 30 this.Controls.Add(btnMyTriSelf); 31 //2.4.为按钮 的 委托 设置一个 方法(按钮被点击的时候 会被 执行) 32 btnMyTriSelf.AddClickMethod(MyTripleClick); 33 34 35 //3.2.设置 按钮的 文本 和位置 36 btnMyTriSelfEvent.Text = "事件 自定义按钮"; 37 btnMyTriSelfEvent.Location = new Point(100, 200); 38 btnMyTriSelfEvent.Size = new Size(150, 50); 39 //3.3.将按钮 显示到 窗体上 40 this.Controls.Add(btnMyTriSelfEvent); 41 //3.4.为按钮 的 委托 设置一个 方法(按钮被点击的时候 会被 执行) 42 btnMyTriSelfEvent.dgMyClick += MyTripleClick; 43 btnMyTriSelfEvent.dgMyClick -= MyTripleClick; 44 } 45 46 void MyTripleClick(DateTime clickTime) 47 { 48 MessageBox.Show("Test" + clickTime); 49 } 50 51 private void btnTrick_Click(object sender, EventArgs e) 52 { 53 //直接 把 按钮对象 里的委托 设置为空,从而 导致委托中原来保存的方法指针都丢失,有可能破坏了 业务的完整性,不安全!!! 54 btnMyTriple.dgMyClick = null; 55 //新式 按钮,没有把按钮里的 委托对象 暴露给 外部直接操作,所以相对来说 比较安全!!! 56 //btnMyTriSelf.RemoveClickMethod( 57 //事件 按钮,事件机制 会自动的将 修饰的 委托变量改为私有,并同时提供一个 add 和 remove方法 58 //btnMyTriSelfEvent.dgMyClick = null; 59 } 60 }
十二. 事件
当我们使用event关键字修饰一个类里的委托对象时,编译器会自动把这个委托对象私有化,并且声称一个与委托对象同名的事件语法,其中就包含了add和remove方法。在类的外部调用这个委托名的时候,实际上就是调用了同名的事件语法,+=就是调用了语法里的add方法,-=就是调用了事件语法里的remove方法,而add和remove方法内部,都是实际操作的私有化的委托对象。
十三. 事件案例(三击按钮)
代码示例:
1 /// <summary> 2 /// 三击按钮类 - 用户点击三次后 执行用户的 方法 3 /// </summary> 4 public class MyTripleButton:System.Windows.Forms.Button 5 { 6 Timer time = new Timer(); 7 8 public MyTripleButton() 9 { 10 base.Click += MyTripleButton_Click; 11 time.Interval = 1000; 12 time.Tick += time_Tick; 13 } 14 15 int clickTimes = 0; 16 //定义一个 用来保存 用户方法的委托对象 17 public event DGMyClick dgMyClick; 18 19 void time_Tick(object sender, EventArgs e) 20 { 21 clickTimes = 0; 22 } 23 24 //每当被点击的时候,此方法会被调用 25 void MyTripleButton_Click(object sender, EventArgs e) 26 { 27 //如果点击次数没达到3次 28 if (clickTimes < 2) 29 { 30 //如果是第一次点击 则启动计时器 31 if (clickTimes == 0) 32 { 33 time.Start(); 34 } 35 //点击次数++ 36 clickTimes++; 37 } 38 else//点击三次后 39 { 40 //1.执行用户的 方法 41 if (dgMyClick != null) 42 dgMyClick(DateTime.Now); 43 //2.清空点击次数 44 clickTimes = 0; 45 //3.重启计时器 46 time.Stop(); 47 } 48 } 49 } 50 51 public partial class Form1 : Form 52 { 53 public Form1() 54 { 55 InitializeComponent(); 56 //1.创建三击按钮对象 57 MyTripleButton myBtn = new MyTripleButton(); 58 //2.利用一个事件机制 为 按钮里的委托对象 注册一个 方法(或 移除一个方法) 59 myBtn.dgMyClick += ClickSelf; 60 //3.注意:因为使用了事件机制 封装了 按钮里的委托对象,所以不能 直接 赋值 和 调用委托了 61 //myBtn.dgMyClick = null; 62 //myBtn.dgMyClick(); 63 this.Controls.Add(myBtn); 64 } 65 66 void ClickSelf(DateTime time) 67 { 68 MessageBox.Show("三击了~~~~~~~~~~~~~!加分!"); 69 } 70 }