• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

第二章:C#.NET面向对象——面向对象程序设计5(委托与事件) ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

 

<五> 委托与事件

 

1、委托

   C#的委托相当于C/C++中的函数指针,函数指针用指针获取一个函数的入口地址,实现对函数的操作。委托是面向对象的,是引用类型,因此对委托的使用要先定义后实例化,最后才调用。委托与C/C++有本质的区别:C/C++中的函数指针仅仅是一个内存地址,其中不包含任何有关函数的参数、返回值以及调用约定等方面的信息。

 

2、声明委托

   C#使用关键字delegate声明委托类型。格式如下:

   [修饰符] delegate 结果类型 委托名称([形式参数列表]);

   ◆ 访问修饰符为可选项,可以使用new、public、private、protected和internal之一。

   ◆ 结果类型指委托对应方法的返回值的类型。

   ◆ 形式参数列表指定委托对应方法的参数。注意,如果参数是一个指针,则必须用unsafe修饰符声明委托。

 

3、委托的实现

   在C#中,可以通过以下三个步骤定义和使用委托:声明、建立委托实例、实例化和调用。上面已经介绍了如何声明一个委托,下面介绍后两个步骤,即建立委托实例、实例化和调用。

   delegate语句定义委托的参数类型(形式参数列表的参数类型)和返回值类型(结果类型),任何具有匹配参数类型和返回值类型的方法均可用来建立此委托的实例。因此,声明了一个委托后,为了实现这个委托,可以按如下的步骤操作:

 (1)建立委托实例:委托实例是委托所要调用的方法,它与声明的委托在以下三个方面必须相同。

◆ 方法类型。如果委托被声明的为void(无返回值)结果类型,则委托实例(方法)必须是void类型;如果委托声明的是其它结果类型(如int类型),则委托实例(方法)返回值类型必须与之对应。

      ◆ 参数类型。委托声明的参数列表类型必须与委托实例(方法)的参数列表类型相同。

      ◆ 参数个数。委托声明的参数列表个数必须与委托实例(方法)的参数列表个数相同。

   除上述三个方面外,其他方面,例如实例与委托的名称、参数名称不必相同。只要这三个方面相同,就可以作为一个委托的实例过程。

 (2)实例化和调用:类名 类的对象名=new 类名();

                   委托名 委托的对象名=new 委托名(类的对象名.方法名)

               委托的对象名=类的对象名.方法名

 

   下面通过一个实例说明如何使用委托

using System;

using System.Collections;

namespace 笔记

{

   public class SimpleClass

    {

        public class WorkerClass

        {

            //建立委托实例:非静态方法

            public int InstanceMethod(int nID, string sName)

            {

                int retval = 0;

                retval = nID * sName.Length;

                Console.WriteLine("调用InstanceMethod方法");

                return retval;

            }

 

            //建立委托实例:静态方法

            static public int StaticMethod(int nID, string sName)

            {

                int retval = 0;

                retval = nID * sName.Length;

                Console.WriteLine("调用StaticMethod方法");

                return retval;

            }

        }

 

        //定义委托,返回类型、参数与上面两个方法相同

       public delegate int SomeDelegate(int nID, string sName);

 

       public static void MMain()

       {

           //调用委托实例:非静态方法

           WorkerClass wr = new WorkerClass();

           SomeDelegate d1=new SomeDelegate(wr.InstanceMethod);

           Console.WriteLine("Invoking delegate InstanceMethod,return={0}",d1(5,"aaa"));//红色的为调用委托

 

           //调用委托实例:静态方法

           SomeDelegate d2 = new SomeDelegate(WorkerClass.StaticMethod);

           Console.WriteLine("Invoking delegate StaticMethod,return={0}", d2(5, "bbb"));

       }

    }

}

 

程序运行结果:

调用InstanceMethod方法

Invoking delegate InstanceMethod,return=15

调用StaticMethod方法

Invoking delegate StaticMethod,return=15

   从上面程序可以看出,使用委托,首先要用new关键字创建一个委托对象,同时为委托对象指明引用的方法名称,然后就可在程序中像使用方法名调用方法一样用委托对象引用方法。

 

4、多播

   相对于上面的一次委托只调用一个方法,一次委托也可以调用多个方法,称为多播。通过“+”和“-”运算符实现多播的增加或减少。

   多播示例。

using System;

using System.Collections;

namespace 笔记

{

   public class SimpleClass

    {

        public class WorkerClass

        {

            //建立委托实例:非静态方法

            public int InstanceMethod(int nID, string sName)

            {

                int retval = 0;

                retval = nID * sName.Length;

                Console.WriteLine("调用InstanceMethod方法");

                return retval;

            }

 

            //建立委托实例:静态方法

            static public int StaticMethod(int nID, string sName)

            {

                int retval = 0;

                retval = nID * sName.Length;

                Console.WriteLine("调用StaticMethod方法");

                return retval;

            }

        }

 

       //定义委托,返回类型、参数与上面两个方法相同

       public delegate int SomeDelegate(int nID, string sName);

       public static void MMain()

       {

           //调用委托实例:非静态方法

           WorkerClass wr = new WorkerClass();

           SomeDelegate d1=new SomeDelegate(wr.InstanceMethod);

           Console.WriteLine("Invoking delegate InstanceMethod,return={0}",d1(5,"aaa"));

 

           //调用委托实例:静态方法

           SomeDelegate d2 = new SomeDelegate(WorkerClass.StaticMethod);

           Console.WriteLine("Invoking delegate StaticMethod,return={0}", d2(5, "bbb"));

 

           //多播

           Console.WriteLine();

           Console.WriteLine("---------测试多播----------");

 

           //多播d3由两个委托d1和d2组成

           SomeDelegate d3 = d1 + d2;

           Console.WriteLine("多播d3由两个委托d1和d2组成:{0}", d3(5, "ccc"));

 

           //委托中的方法个数

           int Num_Method = d3.GetInvocationList().Length;

           Console.WriteLine("多播委托d3中的方法个数:{0}", Num_Method);

 

           //多播d3减去委托d2

           d3 = d3 - d2;

           Console.WriteLine("多播d3减去委托d2:{0}", d3(5, "ddd"));

 

           //委托中的方法个数

           Num_Method = d3.GetInvocationList().Length;

           Console.WriteLine("多播委托d3中的方法个数:{0}", Num_Method);

       }

    }

}

程序运行结果:

调用InstanceMethod方法

Invoking delegate InstanceMethod,return=15

调用StaticMethod方法

Invoking delegate StaticMethod,return=15

 

---------测试多播----------

调用InstanceMethod方法

调用StaticMethod方法

多播d3由两个委托d1和d2组成:15

多播委托d3中的方法个数:2

调用InstanceMethod方法

多播d3减去委托d2:15

多播委托d3中的方法个数:1

   多播开始的时候,d3中有两个委托d1和d2,d3(5,“ccc”)调用等于后一个d2(5,“ccc”)。从d3中减去d2,d3中只有一个方法d1,d3(5,“ddd”)调用等d1(5,“aaa”)。D3.GetInvocationList().Length表示的是多播实例中委托包含的方法个数。委托是从System.Delegate类派生而来,多播则派生于System.Delegate的派生类System.MulticastDelegate。对于下面的代码:

   SomeDelegate d3=d1+d2;

   也可以用Delegate.Combine方法写成:

   SomeDelegate d3=(SomeDelegate) Delegate.Combine(d1,d2);

   也可以使用MulticastDelegate.Combine的方法写成:

   SomeDelegate d3=(SomeDelegate)MulticastDelegate.Combine(d1,d2);

说明:

Combine方法。将指定的可组合多播委托列表连接起来。

Remove方法。从一个委托的调用列表中移除另一个委托的调用列表。例如:

   //合并

   MyDelegate newDelegate=MyDelegate.Combine(delegate1,delegate2);

   //删除

   MyDelegate newDelegate=MyDelegate.Remove(delegate1,delegate2);

 

5、事件

   事件作为C#中的一种类型,为类和类的实例定义发出通知的能力,从而将事件和可执行代码捆绑在了一起。事件最常见的用途是用于窗体编程,当发生像点击按钮、移动鼠标等事件时,相应的程序将收到通知,再执行代码。

   C#事件是按“发布-预订”的方式工作。先在一个类中公布事件,然后就可以在任意数量的类中对事件预订。事件的工作过程可以用下图表示。

 

事件TextOut

事件TextOut的委EventHandler

处理事件的方法CatchEvent

处理事件的方法InstanceCatch

   C#事件机制是基于委托实现的,因此要首先定义一个委托EventHandler:

   Public delegate void EventHandler(object from,myEventArgs e)

   它有两个参数,第一个参数为发送者,第二个参数为事件参数,是从.NET构架的EventArgs类派生的。

   在.NET框架中,所有的事件处理都具有这种模式。由第一个参数指明触发事件的对象,由第二个参数包含事件信息,而且没有返回值。对于用户自定义事件处理,则可以使用任意类型的委托类型。

 

定义事件格式:

[修饰符] event事件的委托名 事件名;

[修饰符] event 事件的委托名 {访问函数声明代码};

可选修饰符包括:abstract,new,override,static,virtual五个访问修饰符之一。不能既包含abstract修饰符又包含事件访问函数声明。包含extern修饰符时,就成为外部事件。因为外部事件声明不提供任何实际的实现代码,所以也不能既包含extern修饰符又包含事件访问函数声明。

访问函数声明代码用于添加或移除客户代码中的事件处理程序。访问方法为add和remove,两者必须同时定义。

事件声明的类型必须是委托类型。

创建事件的一般步骤如下:

 (1)创建一个类(提供数据类):它包含“处理事件类”可以使用的数据(即是向“处理事件类”中的方法提供数据)。该类继承EventArgs类。如:

    //定义MyEventArgs类——提供数据类:该类包含“处理事件类”可以使用的数据

    public class MyEventArgs : EventArgs

    {

        private string StrText;

        public MyEventArgs(string Str)

        {

            this.StrText = Str;

        }

        public string GetStrText

        {

            get { return StrText; }

        }

    }

   (2) 创建一个类(引发事件类):该类用来引发事件。在这个类中要做下如下工作:

   ◆ 声明委托:public delegate void EventHandler(object from, MyEventArgs e)

      其中,EventHandler是委托名称,它有两个参数object from和MyEventArgs e。object from表示引发事件的源;MyEventArgs e表示一个类,它包“处理事件类”可以使用的数据,这个类由EventArgs类派生来。

   ◆ 声明事件:[修饰符] event事件的委托名 事件名。

   ◆ 写方法:该方法用来激活事件的。(即时编写激活事件的方法)如:

    //定义EventSource类——引发事件类:该类的主要作用是用来引发事件

    class EventSource

{

     //MyEventArgs是一个类,包含事件处理程序可以使用的数据

        MyEventArgs Evargs = new MyEventArgs("触发事件");

 

        public delegate void EventHandler(object from, MyEventArgs e);    //定义委托

 

        public event EventHandler TextOut;                               //定义事件

 

        //激活事件的方法

        public void TriggerEvent()

        {

            if (TextOut != null)

                TextOut(this,Evargs);

        }

    }

 

 (3)创建一个类(处理事件类):该类包含处理事件的一个或多个方法。如:

    //定义TestApp类——处理事件类:该类包含处理事件的方法CatchEvent和InstanceCatch

    public class TestApp

    {

        //处理事件的静态方法

        public static void CatchEvent(object from, MyEventArgs e)

        {

            Console.WriteLine("CatchcEvent:{0}", e.GetStrText);

        }

        //处理事件的方法

        public void InstanceCatch(object form, MyEventArgs e)

        {

            Console.WriteLine("InstanceCatch:{0}", e.GetStrText);

        }

     }

 (4)创建一个类(执行类):必须将事件处理程序和事件关联起来,以便事件发生时执行该处理程序。要实现关联,必须首先包含事件的对象,形式为:

   包含事件的类名 对象名=new 包含事件的类名();

创建对象之后就可使用事件,每当包含事件的类的对象的set被调用时,就会创建对象和引发对象。声明一个包含事件的对象之后,使用“+=”运算符可关联事件处理程序和该对象,形式为:

ObjectWithEventName.EvenObj+=new EventDelegate(EventName);

其中,ObjectWithEventName为使用事件类声明的对象;EventObj为事件名称;“+=”为一个指示器,用于指出接下来要将一个事件处理程序加入到事件中;new指出应创建接下来的事件处理程序;EventDelegateName为事件处理程序的名称。如:

    //定义DiaoYong类:执行“处理事件的方法CatchEvent和InstanceCatch”

    public class DiaoYong

    {

        public static void MMain()

        {

            EventSource evsrc = new EventSource();              //实例化“引发事件类”

            TestApp theApp = new TestApp();                     //实例化“处理事件类”

            evsrc.TextOut += new EventSource.EventHandler(theApp.InstanceCatch);   //绑定InstanceCatch方法到事件

            evsrc.TriggerEvent();     //触发事件

            evsrc.TextOut -= new EventSource.EventHandler(theApp.InstanceCatch);   //取消事件与处理事件方法的关联

            evsrc.TextOut += new EventSource.EventHandler(TestApp.CatchEvent);     //绑定CatchEvent方法到事件

            evsrc.TriggerEvent();     //触发事件

        }

    }

 

下面通过一个实例说明如何创建事件并使用事件

using System;

using System.Collections;

namespace 笔记

{


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
c语言数据结构:用标志位实现循环队列发布时间:2022-07-14
下一篇:
C语言的设计模式-单一职责发布时间:2022-07-14
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap