黑马程序员-.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培训、期待与您交流! -------

0 0
原创粉丝点击