黑马程序员-.NET基础之委托和事件

------- 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培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值