前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#委托与事件

C#委托与事件

作者头像
拾点阳光
发布2018-05-10 18:08:39
9620
发布2018-05-10 18:08:39
举报
文章被收录于专栏:码云1024码云1024

Delegate


  C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。委托特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

声明委托

  声明委托的语法如下:

代码语言:javascript
复制
delegate return-type delegate-name (parameter-list);

  其中:return-type是返回类型,delegate-name是委托名字,parameter-list是参数列表。

代码语言:javascript
复制
public delegate int MyDelegate (string s); //声明一个全局的委托,参数为string类型

方法名作为参数


  委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-else(switch)语句,同时使得程序具有更好的可扩展性。

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate
{
    //定义委托,它定义了可以代表的方法的类型
    public delegate void GreetingDelegate(string name);
    class Program
    {
        private static void EnglishGreeting(string name)
        {
            Console.WriteLine("Morning, " + name);
        }

        private static void ChineseGreeting(string name)
        {
            Console.WriteLine("Zao Shang Hao, " + name);
        }

        //注意此方法,它接受一个GreetingDelegate类型的方法作为参数
        private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
        {
            MakeGreeting(name);
        }

        static void Main(string[] args)
        {
            GreetPeople("Jimmy Zhang", EnglishGreeting); //方法名作为参数传递
            GreetPeople("Zhang Ming", ChineseGreeting);
        }
    }
}

实例化委托


  一旦声明了委托类型,委托对象必须使用 new 关键字来创建,且与一个特定的方法有关。当创建委托时,传递到 new 语句的参数就像方法调用一样书写,但是不带有参数。例如:

代码语言:javascript
复制
public delegate void printString(string s);
//...
printString ps1 = new printString(WriteToScreen);//实例化委托WriteToScreen方法
printString ps2 = new printString(WriteToFile);

实例如下:

代码语言:javascript
复制
using System;
namespace DelegateApp
{
    delegate int NumberChanger(int n);
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }
        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }
        static void Main(string[] args)
        {
            // 创建委托实例
            NumberChanger nc1 = new NumberChanger(AddNum);
            NumberChanger nc2 = new NumberChanger(MultNum);
            // 使用委托对象调用方法
            nc1(25);
            Console.WriteLine("Value of Num: {0}", getNum());// 35
            nc2(5);
            Console.WriteLine("Value of Num: {0}", getNum()); //175
            Console.ReadKey();
        }
    }
}

将方法绑定到委托


  可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。委托对象可使用 "+=" 运算符进行合并。只有相同类型的委托可被合并。"-=" 运算符可用于从合并的委托中移除组件委托。

代码语言:javascript
复制
using System;
namespace DelegateAppl
{
    delegate int NumberChanger(int n);
    class TestDelegate
    {
        static int num = 10;
        public static int AddNum(int p)
        {
            num += p;
            return num;
        }

        public static int MultNum(int q)
        {
            num *= q;
            return num;
        }
        public static int getNum()
        {
            return num;
        }

        static void Main(string[] args)
        {
            // 创建委托实例
            NumberChanger nc;
            NumberChanger nc1 = new NumberChanger(AddNum);
            NumberChanger nc2 = new NumberChanger(MultNum);
            nc = nc1;
            nc += nc2; //合并委托
            nc(5);
            Console.WriteLine("Value of Num: {0}", getNum()); //75
            nc -= nc1;
            nc(5);
            Console.WriteLine("Value of Num: {0}", getNum()); //375
        }
    }
}

事件


  事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。事件是用于进程间通信。

  事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

事件声明


  事件和方法一样具有签名,签名包括名称和参数列表。事件的签名通过委托类型来定义,然后向类中添加事件需要使用 event 关键字,并提供委托类型和事件名称。例如:

代码语言:javascript
复制
public delegate void TestEventDelegate(object sender, System.EventArgs e);
public class EventSource
{
    public event TestEventDelegate TestEvent; //基于上面的委托
    private void RaiseTestEvent() { /* ... */ }
}

  事件可标记为 public、private、protected、internal 或 protected internal。这些访问修饰符定义类的用户访问事件的方式。  

自定义事件


  C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:

代码语言:javascript
复制
public delegate void MyEventHandler(object sender, MyEventArgs e);

  其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。

  结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:

  1. 定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
  2. 定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
  3. 定义"事件处理方法,它应当与delegate对象具有相同的参数和返回值类型"。
  4. 用event关键字定义事件对象,它同时也是一个delegate对象。
  5. 用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
  6. 在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
  7. 在适当的地方调用事件触发方法触发事件。

  以下代码的功能是输入'a'触发事件。

代码语言:javascript
复制
using System;
public class EventTest
{
    // 步骤1,定义delegate对象
    public delegate void MyEventHandler(object sender, System.EventArgs e);
    // 步骤2(定义事件参数类)省略
    public class MyEventCls
    {
        // 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类型
        public void MyEventFunc(object sender, System.EventArgs e)
        {
            Console.WriteLine("My event is ok!");
        }
    }
    // 步骤4,用event关键字定义事件对象
    private event MyEventHandler myevent;
    private MyEventCls myecls;
    public EventTest()
    {
        myecls = new MyEventCls();
        // 步骤5,用+=操作符将事件添加到队列中
        this.myevent += new MyEventHandler(myecls.MyEventFunc);
    }
    // 步骤6,以调用delegate的方式写事件触发函数
    protected void OnMyEvent(System.EventArgs e)
    {
        if (myevent != null)
            myevent(this, e);
    }
    public void RaiseEvent()
    {
        EventArgs e = new EventArgs();
        // 步骤7,触发事件
        OnMyEvent(e);
    }
    public static void Main()
    {
        EventTest et = new EventTest();
        Console.Write("Please input ''a'':");
        string s = "a";
        Console.WriteLine(s);
        if (s == "a")
        {
            et.RaiseEvent();
        }
        else
        {
            Console.WriteLine("Error");
        }
    }
}

匿名方法


  委托是用于引用与其具有相同标签的方法。换句话说,使用委托对象调用可由委托引用的方法。

  匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法,不需要指定返回类型,它是从方法主体内的 return语句推断的。

  匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:

代码语言:javascript
复制
delegate void NumberChanger(int n);
NumberChanger nc = delegate(int x)
{
    Console.WriteLine("Anonymous Method: {0}", x);
};

  委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。如上面的可以调用为:

代码语言:javascript
复制
nc(1);

  下面通过实例来了解一下匿名方法。

代码语言:javascript
复制
using System;
namespace test
{
    delegate void Printer(string s); //声明委托
    class TestClass
    {
        static void Main()
        {
            // 初始化匿名方法
            Printer p = delegate(string j)
            {
                System.Console.WriteLine(j + " anonymous method");
            };
            p("The delegate using the method is called:"); //调用匿名方法
            p = new Printer(TestClass.DoWork); //实例化委托
            p("using the named method is called: ");//调用DoWork方法
        }
        static void DoWork(string k)
        {
            System.Console.WriteLine(k + "dowork");
        }
    }
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-08-04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Delegate
  • 方法名作为参数
  • 实例化委托
  • 将方法绑定到委托
  • 事件
  • 事件声明
  • 自定义事件
  • 匿名方法
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档