• 委托:一种引用类型,这种类型可以用来定义方法签名,从而使用委托实现将方法作为参数传递给其他方法。类似于C++中的函数之争,使用委托使程序员可以将方法引用封装在委托对象内
    1. 定义和声明委托:

       delegate 返回值 委托名(参数列表);
      eg:
      public delegate void SayHelloDelegate(string name);
    2. 使用委托:委托其实通过返回值和参数列表来定义方法和签名。任何与委托具有相同返回值和参数列表(签名)的方法都可以赋给该委托。
       public delegate void SayHelloDelegate(string name);//声明委托
      public viod SayHelloEnglish(string name)//声明方法
      {
      Console.WriteLine("Hello,{0}",name);
      }
      SayHelloDelegate s=SayHelloEnglish;//把方法赋予委托对象

      委托实例化的几种形式:

       .使用new关键字
      SayHelloDelegate s=new SayHelloDelegate(SayHelloEnglish);
      .直接赋值
      SayHelloDelegate s=SayHelloEnglish;
      .使用匿名方法
      SayHelloDelegate s=delegate(string name)
      {
      Console.WriteLine("Hello,{0}",name);
      }
      .使用Lambda表达式,例如:
      SayHelloDelegate s=name=>
      {
      Console.WriteLine("Hello,{0}",name);
      }
       委托实例化后,就可以像调用方法一样调用委托。eg:
      s("John");
    3. 多播委托:一个委托对象可以包含多个方法。调用多播委托时,按赋值的顺序一次执行每个方法。
      1. 实现多播委托
        1. 使用运算符+实现:委托对象的一个有用属性,可以使用+运算符将多个对象分配给一个委托实例。这里运算符+操作对象只能是委托对象,不能使方法签名。

           public delegate void SayHelloDelegate(string name);
          public void SayHelloEnglish(string name)
          {
          Console.WriteLine("Hello,{0}",name);
          }
          public void SayHelloChinese(string name);
          {
          Console.WriteLine("你好,{0}",name);
          }
          SayHelloDelegate s1=new SayHelloDelegate(SayHelloEnglish);
          SayHelloDelegate s2=SayHelloChinese;
          SayHelloDelegate s3=s1+s2;//操作符两边只能是委托对象
        2. 使用运算符+=
           public delegate void SayHelloDelegate(string name);
          public void SayHelloEnglish(string name)
          {
          Console.WriteLine("Hello,{0}",name);
          }
          public void SayHelloChinese(string name);
          {
          Console.WriteLine("你好,{0}",name);
          }
          SayHelloDelegate s=new SayHelloDelegate(SayHelloEnglish);
          SayHelloDelegate s+=SayHelloChinese;//右边的操作对象是方法签名
      2. 特点:
        1. 多播委托可以包含多个方法
        2. 多播委托包含的方法必须返回viod,否则会抛出run-time exception
        3. 只能合并相同类型的委托
      3. 从多播委托中移除组件
        1. 使用运算符-,两侧必须为委托对象
        2. 使用运算符-=,右侧可以为方法名
    4. 匿名方法:相对于命名方法(以前接触的方法都是命名方法,都是包含方法名的)来说,顾名思义,就是没有命名的方法,就是作为一个整体的一些代码块。想要实现匿名方法,使用委托是唯一途径,把整个代码块作为参数赋予委托对象,然后调用委托对象,从而实现方法的执行。
       //创建委托
      delegate void Del(string name);
      //实现匿名方法
      Del d=delegate(string name)
      { //code block
      };

summary:

      1. 匿名方法的定义是以关键字delegate开始,后面跟着参数列表和方法主体。
      2. 使用匿名方法,可以省去一个编写方法的过程,从而使代码看起来更简洁。
      3. 使用匿名方法可以减少实例化委托的代码系统开销。

attention:

      1. 在匿名方法中不能使用跳转语句跳转到该匿名方法的外部,匿名方法外部的跳转语句不能跳转到匿名方法的内部。
      2. 匿名方法内部不能访问不安全代码。
      3. 不能再匿名方法外部使用ref和out参数,但可以使用在匿名方法外部定义的其他变量。
         public delegate void MyDelegate();
        class Program
        {
        static void Main(string[] args)
        {
        Program p=new Program();
        for(int i=;i<=;i++)
        p.TestInstanceDataMembers();
        Console.ReadLine();
        }
        //测试方法
        public void TestInstanceDataMembers()
        {
        //声明匿名方法并赋值给对象
        MyDelegate d=delegate
        {
        //操作外部局部变量
        Console.WriteLine("Count:{0}",++m_iCount);
        };
        d();//执行方法
        }
        public int m_iCount=;//定义外部变量
        }
         Count:
        Count:
        Count:
        Count:
        Count:
    1. 委托中的协变和逆变:将方法签名与委托类型匹配时,协变和逆变提供了一定程度的灵活性。协变允许方法具有的派生返回类型比委托中定义的更多。逆变允许方法具有的派生参数类型比委托类型中的更少
      1. 协变方法签名与委托类型匹配的灵活性体现在方法返回类型上。eg:

         Shape
        {
        //类主体
        }
        Point:Shape
        {
        //类主体
        }
        delegate Shape DoDelegate();
        static Shape FirstDo()
        {
        return null;
        }
        static Point SecondDo()
        {
        return null;
        }
        DoDelegate Do1=FirstDo;
        //协变允许实现这样的委托
        DoDelegate Do2=SecondDo;

        协变允许返回类型更多体现在类的派生上

      2. 逆变使方法签名与委托类型匹配的灵活性体现在参数上。eg:
         System.DateTime lastActivity;
        public From1()
        {
        InitializeComponent();
        lastActivity=new System.DateTime();
        this.textBox1.KeyDown+=this.MultiHandler;//触发KeyEventArgs
        this.botton1.MouseClick+=this.MultiHandler;//触发MouseEventArgs
        }
        private void MultiHandler(object sender,System.EvenArgs e)
        {
        lastActivity=System.DateTime.Now;
        }

        以上代码中只创建一个接收EventArgs输入参数的事件处理程序,然后,可以将该处理程序与发送MouseEventArgs类型作为参数的Botton.MouseClick事件一起使用,也可以将该处理程序与发送KeyEventArgs参数的TextBox.KeyDown事件一起使用。

    2. 回调函数:委托实例化后,就可以将其作为参数进行传递,方法遍可以将一个委托作为参数来接受,并且以后可以调用该委托,这称为回调函数
       delegate void Del(string name);//定义委托
      void Do(string)//定义方法
      {
      }
      void FirstDo(string s,Del del)//把委托作为参数传递进来
      {
      del(s);//调用委托
      }
      Del del=new Del(Do);//初始化委托
      FirstDo(“你好",del);//调用方法
      1. 属于工作流的一个部分
      2. 必须按照工作流指定的调用约定来声明(定义)
      3. 它的调用时机由工作流决定,回调函数的实现者不能直接调用回调函数来实现工作流的功能。
      4. 使用回调机制,可以为工作流实现扩展。可以把工作流中需要用户干预的,或者需要提供给用户的数据以回调的模式提供给用户。而用户不需要知道整个工作的流程,只需要知道回调函数的说明就可以使用工作流模块提供的功能,这对信息的隐藏也是有作用的。
         class SumClass
        {
        public delegate int Sum(int num1,int num2);委托
        public int SumAll(int num1,int num2,Sum sun)//把委托实例作为参数传递
        {
        return sun(num1,num2);//回调函数
        }
        }
        class Program
        {
        static void Main(string[] args)
        {
        Program prog=new Program();
        SumClass sumClass=new SumClass();
        int result=sunClass.SumAll(,,prog.GetSum);//回调函数GetSum
        Console.WriteLine(result.ToString());
        Console.ReadLine();
        }
        private int GetSum(int a,int b)
        {
        return a+b;
        }
        }
  • 事件:所有与用户交互的过程基本上都是事件触发和处理的过程。
    1. 定义和声明事件:事件其实就是消息,事件的本质就是对消息的封装,用作对象之间的通信:事件的发起方称为事件发送器,事件的接收方称为事件的接收器。在C#中提供了一个名为EventHandler的预定义委托,专用于表示不生成数据的事件的事件处理方法

      这个委托的预定义如下:
      public delegate void EventHandler(
      object sender,//引用引发事件的实例
      EventArgs e//从EventArgs类型派生,保存事件数据
      )

      EventHandler专用于表示不生成数据的事件的事件处理程序方法。如果事件生成数据,则必须提供自定义数据类型,并创建一个委托,其中第二个参数为自定义参数类型。若要将事件与处理事件的方法关联,还要向事件添加委托的实例,这时需要使用关键字event来声明。

       //访问修饰符event EventHandler 事件名
      
       public event EventHandler NoDataEventHandler;
      //定义了没有数据的事件成员
    2. 定义事件处理程序:定义了事件成员,就可以把事件处理程序关联到事件上。事件处理程序委托会被绑定到系统引发事件时要执行的方法。事件处理程序会被添加到事件中,以便当事件引发时,事件处理程序能够调用它的方法,这就是订阅事件。订阅事件的处理过程如下:
      1. 编写事件处理程序

         void HandleCustomEvent(object sender,CustomEventArgs a)
        {
        //处理程序
        }
      2. 使用加法赋值运算符+=来为事件附加处理程序,eg:
         publisher.RaiseCustomEvent+=HandlerCustomEvent;

        当然如果用匿名方法或Lambda表达式,可以不用事先编写事件处理程序,直接把匿名方法或Lambda表达式绑定到事件上。事件是一种特殊类型的委托,支持多播委托,因此可以把多个事件处理程序绑定到一个事件中。

      3. 创建并使用事件
         using cs002;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks; namespace cs002
        {
        public class A
        {
        public delegate void EventHandler(object sender, EventArgs e);//定义委托
        public event EventHandler a;//声明事件
        public void Run(EventArgs e)//引发事件
        {
        Console.WriteLine("产生一个事件。");
        a(this, e);//处理当前引发的事件
        }
        }
        class B
        {
        public B(A a)
        {
        a.a += new A.EventHandler(this.b); //订阅事件处理程序
        }
        private void b(object sender,EventArgs e)//事件处理的方法
        {
        Console.WriteLine("处理事件!");
        Console.Read();
        }
        }
        class prg
        {
        public static void Main(string[] args)
        {
        A a = new A();
        B b = new B(a);
        EventArgs e = new EventArgs();
        a.Run(e);
        }
        }
        }
      4. 从EventArgs类派生:EventArgs类是包含事件数据的类的基类。此类不包含事件数据,在事件的引发时不向事件处理程序传递状态信息的事件会使用此类。EventArgs类本身并没有什么可介绍的,它只提供了一个只读字段Empty,表示事件没有数据。如果事件处理需要状态信息,则应用程序必须从EventArgs类派生一个类来保存数据。例如创建一个KeyEventArgs类,该类保存键盘按键事件中要包含的按键信息。
         internal class KeyEventArgs:EventArgs
        {
        private char keyChar;//按键信息
        public KeyEventArgs(char keyChar):base()
        {
        this.keyChar=keyChar;
        }
        public char KeyChar//按键信息
        {
        get
        {
        return keyChar;
        }
        }
        }

        创建并使用派生事件

         using cs002;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks; namespace cs002
        {
        //触发火警事件
        public class FireEventArgs:EventArgs
        {
        public FireEventArgs (string room,int ferocity)
        {
        this.room = room;
        this.ferocity = ferocity;
        }
        public string room;
        public int ferocity;//火势等级
        }
        public class FireAlarm
        {
        public delegate void FireEventHandler(object sender, FireEventArgs fe);
        //创建火警事件委托
        public event FireEventHandler FireEvent;
        //触发火警事件 和委托相联系/类型相同
        public void ActivateFireAlarm(string room,int ferocity)
        {
        FireEventArgs fireArgs = new FireEventArgs(room, ferocity);
        //调用委托
        FireEvent(this, fireArgs);
        }
        }
        //火警事件处理类
        class FireHandlerClass
        {
        public FireHandlerClass (FireAlarm fireAlarm)
        {
        //订阅火警事件处理程序
        fireAlarm.FireEvent += new FireAlarm.FireEventHandler(ExtinguishFire);//多播委托
        }
        //火警事件处理程序
        void ExtinguishFire(object sender,FireEventArgs fe)
        {
        Console.WriteLine("\n火警事件是由{0}引发的:", sender.ToString());
        if (fe.ferocity < )
        Console.WriteLine("发生在{0}的火警是没有问题的,用水就可以浇灭。", fe.room);
        else if (fe.ferocity < )
        Console.WriteLine("要使用灭火器才能扑灭{0}的大火。", fe.room);
        else
        Console.WriteLine("发生在{0}的大火已经失控,请通知政府部门!", fe.room);
        }
        }
        public class program
        {
        public static void Main(string[] args)
        {
        //创建火警对象
        FireAlarm myFireAlarm = new FireAlarm();
        //创建火警事件处理程序对象
        FireHandlerClass myFireHandler = new FireHandlerClass(myFireAlarm);
        //触发火警事件
        myFireAlarm.ActivateFireAlarm("厨房", );
        myFireAlarm.ActivateFireAlarm("书房", );
        myFireAlarm.ActivateFireAlarm("车库", );
        }
        }
        }
      5. 在派生类中引发基类事件:事件作为类的成员,一般都被定义为public类型,但是派生类并不能直接调用基类中声明的事件。但可以在包含该事件的基类中创建一个受保护的调用方法。通过调用或重写此调用方法,派生类便可简介调用该事件。
         using cs002;
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading.Tasks; namespace cs002
        {
        public class ShapeEventArgs:EventArgs
        {
        private double newArea;
        public ShapeEventArgs(double d)
        {
        newArea = d;
        }
        public double NewArea
        {
        get { return newArea; }
        }
        }
        //定义图形类Shape,在该类中定义事件
        public abstract class Shape
        {
        public delegate void shapeEventHandler(object sender, ShapeEventArgs e);
        public double area;
        public event shapeEventHandler ShapeChanged;
        public abstract void Draw();
        //受保护的触发事件的方法
        protected virtual void OnShapeChanged(ShapeEventArgs e)
        {
        shapeEventHandler handler = ShapeChanged;
        if(handler!=null)
        {
        handler(this, e);
        }
        }
        }
        public class Circle:Shape
        {
        private double radius;
        public Circle(double d)
        {
        radius = d;
        area = 3.14 * d * d;
        }
        public void Update(double d)
        {
        radius = d;
        area = 3.14 * d * d;
        OnShapeChanged(new ShapeEventArgs(area));//触发事件
        }
        //重写受保护的触发事件的方法
        protected override void OnShapeChanged(ShapeEventArgs e)
        {
        //调用基类的方法以触发事件
        base.OnShapeChanged(e);
        }
        public override void Draw()
        {
        Console.WriteLine("画一个圆");
        }
        }
        //事件处理程序
        public class ShapeHandler
        {
        public ShapeHandler (Shape s)
        {
        //订阅事件
        s.ShapeChanged += new Shape.shapeEventHandler(HandleShapeChanged);
        }
        //事件处理程序
        private void HandleShapeChanged(object sender,ShapeEventArgs e)
        {
        Shape s = (Shape)sender;
        //显示图形信息
        Console.WriteLine("图形更改事件是由{0}引起的,图形的面积更新后是:{1}", sender.ToString(), e.NewArea);
        //绘制图形
        s.Draw();
        }
        }
        public class program
        {
        public static void Main(string[] args)
        {
        Circle c1 = new Circle();
        Console.WriteLine("更新前的图形面积是:{0}", c1.area);
        ShapeHandler myShapeHandler = new ShapeHandler(c1);
        c1.Update();
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
        }
        }
        }
      6. 实现接口事件
         public interface Iobject//定义接口
        {
        event EventHandler OnChangedEventArgs;
        }
        public class MyEventArgs:EventArgs
        {
        }
        public class A:Iobject
        {
        public event EventHandler OnChangedEventArgs;
        void ChangedA{
        OnChanged(new MyEventArgs(/*arguments*/));
        }
        protected virtual void OnChanged(MyEventArgs e)
        {
        if(OnChangedEventArgs!=null)
        {
        OnChangedeEventArgs(this,e);
        }
        }
        }

c#学习笔记03——委托和事件的更多相关文章

  1. [读书笔记]C#学习笔记二: 委托和事件的用法及不同.

    前言:  C#委托是什么 c#中的委托可以理解为函数的一个包装, 它使得C#中的函数可以作为参数来被传递, 这在作用上相当于C++中的函数指针. C++用函数指针获取函数的入口地址, 然后通过这个指针 ...

  2. 机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理、源码解析及测试

    机器学习实战(Machine Learning in Action)学习笔记————03.决策树原理.源码解析及测试 关键字:决策树.python.源码解析.测试作者:米仓山下时间:2018-10-2 ...

  3. OpenCV 学习笔记03 边界框、最小矩形区域和最小闭圆的轮廓

    本节代码使用的opencv-python 4.0.1,numpy 1.15.4 + mkl 使用图片为 Mjolnir_Round_Car_Magnet_300x300.jpg 代码如下: impor ...

  4. OpenCV 学习笔记03 findContours函数

    opencv-python   4.0.1 1 函数释义 词义:发现轮廓! 从二进制图像中查找轮廓(Finds contours in a binary image):轮廓是形状分析和物体检测和识别的 ...

  5. C++ GUI Qt4学习笔记03

    C++ GUI Qt4学习笔记03   qtc++spreadsheet文档工具resources 本章介绍创建Spreadsheet应用程序的主窗口 1.子类化QMainWindow 通过子类化QM ...

  6. SaToken学习笔记-03

    SaToken学习笔记-03 如果排版有问题,请点击:传送门 核心思想 所谓权限验证,验证的核心就是一个账号是否拥有一个权限码 有,就让你通过.没有?那么禁止访问! 再往底了说,就是每个账号都会拥有一 ...

  7. Redis:学习笔记-03

    Redis:学习笔记-03 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 7. Redis配置文件 启动 ...

  8. C#事件和委托(C#学习笔记03)

    委托 1. C# 中的委托类似于 C 或 C++ 中指向函数的指针.委托表示引用某个方法的引用类型变量,运行时可以更改引用对象. 2. 特别地,委托可以用于处理事件或回调函数.并且,所有的委托类都是从 ...

  9. 《C#高级编程》学习笔记------C#中的事件和委托

    本文转载自张子阳 目录 委托的作用 将方法绑定到委托 事件的来由 Observer设计模式 .Net Framework中的委托与事件   引言 委托 和 事件在 .Net Framework中的应用 ...

随机推荐

  1. 【学习Koa】原生koa2 静态资源服务器例子

    实现思路 首先读取当前路径下所有的文件和文件夹 当去点击某个列表项时判断其实文件还是文件夹,文件的话直接读取,文件夹则再次利用上一个步骤读取并展示 文件结构 代码 index.js 入口文件 cons ...

  2. Python MySQL Delete

    章节 Python MySQL 入门 Python MySQL 创建数据库 Python MySQL 创建表 Python MySQL 插入表 Python MySQL Select Python M ...

  3. c++程序—if语句实践

    三只小 #include<iostream> using namespace std; #include<string> int main() { //which pig is ...

  4. HDU 1003:Max Sum

    Max Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  5. 爬虫(十八):Scrapy框架(五) Scrapy通用爬虫

    1. Scrapy通用爬虫 通过Scrapy,我们可以轻松地完成一个站点爬虫的编写.但如果抓取的站点量非常大,比如爬取各大媒体的新闻信息,多个Spider则可能包含很多重复代码. 如果我们将各个站点的 ...

  6. Dubbo与Zookeeper 简介

    转自http://blog.csdn.net/congcong68/article/details/41113239 首先说一下Dubbo解决什么问题: (1)当服务越来越多时,服务Url配置管理变得 ...

  7. SPOJ FISHER + FPOLICE SPFA+背包

    当初第一次做的是FPLICE这个题,当时就觉得要用图论去搜索,但是当时陷入死思维就是 dp[][]两个维度都是点,这样就违背了题目的本意,题目给定了一个时间T,在不超过时间T的情况下求最小的消耗,这不 ...

  8. request和response的setCharacterEncoding()方法

    1.pageEncoding=”UTF-8”的作用是设置JSP编译成Servlet时使用的编码.2.contentType=”text/html;charset=UTF-8”的作用是指定服务器响应给浏 ...

  9. MyBatis:一对多、多对一处理

    多对一的处理 多对一的理解: 多个学生对应一个老师 如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师! 数据库设计 CREATE TABLE `teacher` ( `id` INT( ...

  10. POJ 3911:Internet Service Providers

    Internet Service Providers Time Limit: 2MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I ...