黑马程序员-.NET基础之委托和事件
来源:互联网 发布:java graphics类详解 编辑:程序博客网 时间:2024/06/16 10:49
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------
委托和事件作为winform,ASP.NET等不论是B/S,还是C/S开发的应用中是司空见惯的存在,我们初学者只有很好的掌握这些理论基础,才能在后续的学习中灵活且高效的运用这些语言特性。所以现将笔记整理如下。
一、委托
委托是用来处理需用函数指针来处理的情况的
委托是完全面向对象的,是类型安全的
委托是可保存对方法的引用的类。与其他的类不同,委托类具有一个签名,并且它只能对与其签名匹配的方法进行引用。这样,委托就等效于一个类型安全函数指针或一个回调
委托声明定义一个从 System.Delegate 类派生的类。委托实例封装了一个调用列表,该列表列出了一个或多个方法,每个方法称为一个可调用实体。对于实例方法,可调用实体由该方法和一个相关联的实例组成。下面给出委托的实例化和调用示例。
using System;namespace CSharpPractice.Delegate{ delegate void D(int[] A); class ArraySort { public static void DisplayMatrix(int[] A) { //打印矩阵 foreach (int i in A) Console.Write("{0,5} ", i); Console.WriteLine(); } public static void GeneralSort(int[] A, D sort) { sort(A); Console.WriteLine("升序数组: "); foreach (int i in A) Console.Write("{0,5} ", i); Console.WriteLine(); } public static void BubbleSort(int[] A) //冒泡算法 { int i, t; int N = A.Length; //获取数组A的长度N for (int loop = 1; loop <= N - 1; loop++)//外循环进行N-1轮比较 { for (i = 0; i <= N - 1 - loop; i++) //内循环两两比较,大数下沉 if (A[i] > A[i + 1]) { t = A[i]; A[i] = A[i + 1]; A[i + 1] = t; } } } public static void SelectSort(int[] A) //选择算法 { int i, t, MinI; int N = A.Length; //获取数组A的长度N for (int loop = 0; loop <= N - 2; loop++) //外循环进行N-1轮比较 { MinI = loop; for (i = loop; i <= N - 1; i++) //内循环中在无序数中找最小值 if (A[i] < A[MinI]) MinI = i; //最小值与无序数中的第一个元素交换 t = A[loop]; A[loop] = A[MinI]; A[MinI] = t; } } static void Main() { int[] A = new int[10]; Random rNum = new Random(); //数组A赋值(0~100之间的随机数) for (int i = 0; i < A.Length; i++) A[i] = rNum.Next(100); Console.WriteLine("原始数组: "); DisplayMatrix(A); D d1 = new D(ArraySort.BubbleSort);//创建委托实例,指向冒泡算法 Console.Write("冒泡算法---"); GeneralSort(A, d1); D d2 = new D(ArraySort.SelectSort);//创建委托实例,指向选择算法 Console.Write("选择算法---"); GeneralSort(A, d2); Console.ReadKey(); } }}
2.匿名方法委托
无需先声明类或结构以及与委托匹配的方法,而是在创建委托的实例时,直接声明与委托匹配的方法的代码块(匿名方法),匿名方法委托例示。
using System;namespace CSharpPractice.Delegate{ // 声明委托 delegate void Printer(string s); class TestClass { // 与命名委托相关的方法: static void DoWork(string k) { Console.WriteLine(k); } static void Main() { // 使用匿名方法实例化delegate类: Printer p = delegate(string j) { Console.WriteLine(j); }; // 匿名delegate调用结果: p("使用匿名方法的委托的调用."); // 使用"DoWork"方法对delegate实例化: p = new Printer(TestClass.DoWork); // 传统delegate调用结果: p("使用命名方法的委托的调用."); Console.ReadLine(); } }}
3.多播委托
委托也可以包含多个方法,这种委托称为多播委托
如果调用多播委托实例,则按顺序依次调用多播委托实例封装的调用列表中的多个方法
声明多播委托时,其返回类型必须为void,因为无法处理多次调用的返回值,而且不能带输出参数(但可以带引用参数)
多播委托通过 + 或 += 向多播委托实例封装的调用列表中添加方法;通过 – 或 -= 从多播委托实例封装的调用列表中删除方法。
using System;namespace CSharpPractice.Delegate{ delegate void D(int x); class C { public static void M1(int i) { Console.WriteLine("C.M1: " + i); } public static void M2(int i) { Console.WriteLine("C.M2: " + i); } public void M3(int i) { Console.WriteLine("C.M3: " + i); } } class Test { static void Main() { D cd1 = new D(C.M1); cd1(-1); // call M1 D cd2 = new D(C.M2); cd2(-2); // call M2 D cd3 = cd1 + cd2; cd3(10); // call M1 then M2 cd3 += cd1; cd3(20); // call M1, M2, then M1 C c = new C(); D cd4 = new D(c.M3); cd3 += cd4; cd3(30); // call M1, M2, M1, then M3 cd3 -= cd1; // remove last M1 cd3(40); // call M1, M2, then M3 cd3 -= cd4; cd3(50); // call M1 then M2 cd3 -= cd2; cd3(60); // call M1 cd3 -= cd2; // impossible removal is benign cd3(60); // call M1 cd3 -= cd1; // invocation list is empty so cd3 is null // cd3(70); // System.NullReferenceException thrown cd3 -= cd1; // impossible removal is benign Console.ReadLine(); } }}
4.委托的兼容性
D和M的参数数目相同,且各自对应参数具有相同的ref或out修饰符;(D指委托,M指方法)
对于每个ref或out参数,D中的参数类型与M中的参数类型相同。
存在从M的返回类型到D的返回类型的标识或隐式引用转换。即允许方法具有的派生返回类型比委托中定义的更多(协变)。
每一个值参数(没有 ref 或 out 修饰符的参数)都存在从D中的参数类型到M中的对应参数类型的标识或隐式引用转换。允许方法具有的派生参数类型比委托类型中的更少(逆变)
using System;namespace CSharpPractice.Delegate{ class Mammals { //... } class Dogs : Mammals { //... } class Program { // 定义委托 public delegate Mammals HandlerMethod(); public delegate void HandlerMethod1(Mammals m); public delegate void HandlerMethod2(Dogs d); public static Mammals FirstHandler() { Console.WriteLine("first handler"); return null; } public static Dogs SecondHandler() { Console.WriteLine("second handler"); return null; } public static void ThirdHandler(Mammals m) { Console.WriteLine("third handler"); } static void Main() { HandlerMethod handler1 = FirstHandler; handler1(); //协变 HandlerMethod handler2 = SecondHandler; handler2(); Mammals m = new Mammals(); HandlerMethod1 handler11 = ThirdHandler; handler11(m); //逆变 Dogs d = new Dogs(); HandlerMethod2 handler22 = ThirdHandler; handler22(d); Console.ReadKey(); } }}
二、事件
类或对象可以通过事件(event)向其他类或对象通知发生的相关事情。发送(或引发)事件的类称为“发行者”(生产者),接收(或处理)事件的类称为“订户”(消费者)。
事件是一种使对象或类能够提供通知的成员。客户端可以通过提供事件处理程序(event handler)为相应的事件添加可执行代码。
事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。
2.事件特点
发行者确定何时引发事件,订户确定执行何种操作来响应该事件
一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件
没有订户的事件永远不会被调用
事件通常用于通知用户操作,例如,图形用户界面中的按钮单击或菜单选择操作
如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序
可以利用事件同步线程
在 .NET Framework 类库中,事件是基于 EventHandler 委托和 EventArgs 基类的
3.事件处理机制
事件实际上是委托的一种特殊形式。C# 使用一种委托模型来实现事件。事件模型分为事件生产者和事件消费者,其处理机制大致可以分为下列4步
在事件生产者类中声明一个事件成员,即某种事件处理委托(简称为事件委托)的实例(多播事件委托实例);
在事件消费者类中声明与事件委托相匹配的事件处理方法;
通过“+=”向多播事件委托实例封装的调用列表中添加事件处理方法,或通过“-=”从多播事件委托实例封装的调用列表中删除事件处理方法;
在事件生产者类中添加有关发生事件的代码,即当满足某种条件时(发生事件),则调用委托,即调用多播事件委托实例封装的调用列表中添加的事件处理方法。如果没有订阅,即事件实例为Null,则不作任何处理。
下面给出实现事件步骤的代码。算是对初步掌握事件一个总结吧。
<span style="font-size:18px;">using System;using System.Collections;namespace CSharpPractice.Event{ //步骤1:声明提供事件数据的类。 public class NameListEventArgs : EventArgs { public string Name { get; set; } public int Count { get; set; } public NameListEventArgs(string name, int count) { Name = name; Count = count; } } //步骤2:声明事件处理委托。 public delegate void NameListEventHandler(object source, NameListEventArgs args); //步骤3:声明引发事件的类(事件生产类)。 public class NameList { ArrayList list; //步骤4:在事件生产类中,声明事件。 public event NameListEventHandler nameListEvent; public NameList() { list = new ArrayList(); } public void Add(string Name) { list.Add(Name); //步骤5:在事件生产类中,实现产生事件的代码。 if (nameListEvent != null) { nameListEvent(this, new NameListEventArgs(Name, list.Count)); } } } //步骤6:声明处理事件的类(事件消费类)。 public class EventDemo { //步骤7:在事件消费类中,声明事件处理方法。 public static void Method1(object source, NameListEventArgs args) { Console.WriteLine("列表中增加了项目:{0}", args.Name); } //步骤7:在事件消费类中,声明事件处理方法。 public static void Method2(object source, NameListEventArgs args) { Console.WriteLine("列表中的项目数:{0}", args.Count); } public static void Main() { NameList nl = new NameList(); //步骤8:在事件消费类中,订阅或取消事件。 nl.nameListEvent += new NameListEventHandler(EventDemo.Method1); nl.nameListEvent += new NameListEventHandler(EventDemo.Method2); nl.Add("张三"); nl.Add("李四"); nl.Add("王五"); Console.ReadLine(); } }}</span>
写了这么久,好累啊,该出去走走了,放松放松。。
------- Windows Phone 7手机开发、.Net培训、期待与您交流! -------
- 黑马程序员-.NET基础之委托和事件
- 黑马程序员---委托和事件
- 黑马程序员--委托deleegate和事件event
- 黑马程序员_委托和事件
- 黑马程序员--委托事件
- 黑马程序员:C#基础篇(三)委托与事件
- 黑马程序员之委托和多线程
- 黑马程序员--委托与事件
- 黑马程序员------.Net基础之值类型和引用类型
- 黑马程序员-.NET基础之继承和多态
- 黑马程序员-.NET基础之日期和字符串处理
- 黑马程序员-.NET基础之文件和I/O流
- 黑马程序员——04 委托和事件
- .net 委托和事件
- .net事件和委托
- .NET 委托和事件
- .net 委托和事件
- 黑马程序员------.Net基础之接口
- c/c++内存分配方式
- 第八周项目三多分段函数求值
- unity3d游戏开发之整合vs2008来加快unity c#开发
- sql之left join、right join、inner join的区别
- Android NDK的使用
- 黑马程序员-.NET基础之委托和事件
- Java中图片压缩处理
- 第八周项目 2 加班费,要不要 ?
- mac中finder侧边栏的桌面变成Desktop的解决方法
- 第八周项目一分段求值
- 第八周项目4-个人所得税计算器
- 分享几套古典复古式的UI设计
- 第八周项目1-求简单分段函数的值
- 你不知道的Eclipse的用法:使用MAT分析Android的内存