• 委托与事件


    一、委托部分

     “委托”是一种指向一个类的静态方法,或者实例方法的数据结构,委托类似于 C++ 函数指针,但它是类型安全的。委托允许将方法作为参数进行传递,一旦为委托分配了方法,委托就将与该方法具有完全相同的行为。
    委托主要用在两个方面:其一是CallBack(回调)机制;其二是事件处理机制。什么是回调,在上一篇文章中介绍过了。关于事件处理,在本文的后面,也将进行介绍。
    委托可以链接在一起;例如,可以对一个事件调用多个方法。

    委托实现回调的时候,可以直接传方法名,也可以传委托实例,如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            public delegate void DelCout(string str);
    
            static void Main(string[] args)
            {   //直接传方法,在编译的时候,还是会帮我们new,这就是一个语法糖。
                DelMethod(Cout,"直接传方法");
               
                DelMethod(new DelCout(Cout),"传委托实例");
    
                Console.Read();
            }
    
            private static void DelMethod(DelCout delCout,string str)
            {
                delCout(str);
            }
    
            private static void Cout(string str)
            {
                Console.WriteLine(str);
            }
        }
    }

    关于委托的两个主要作用,可能你明白事件要通过委托来实现,这是C#内部定义成这样的,但不清楚为什么回调机制也要用委托。
    在C\C++中,实现回调是通过函数指针来实现的,函数指针其实就是个内存地址,由于该地址不会携带任何其他信息,如函数期望的参数个数、参数类型、返回值类型等,所以这时的回调函数是非类型安全的.C#为回调函数提供了称为委托的机制,其能提供所期望的参数个数、参数类型、返回值类型等信息,因此委托是类型安全的.

    二、事件

    先给个例子:(代码来源:http://www.cnblogs.com/donghaiyiyu/archive/2007/07/29/828738.html)

    using System;
     using System.Collections.Generic;
     using System.Text;
     
     namespace EventDemo
     {
            public delegate void SalaryCompute();        //声明一个代理类
     
            public class Employee
           {
                  public event SalaryCompute OnSalaryCompute;         //定义事件,将其与代理绑定
    
                  public virtual void FireEvent()       //触发事件的方法
                  {
                        if (OnSalaryCompute != null)
                         {
                                OnSalaryCompute();      //触发事件
                         }
                  }
           }
    
           public class HumanResource
           {
                  public void SalaryHandler()          //事件处理函数
                  {
                         Console.WriteLine("Salary");     //只是打印一行字而已
                  }
    
                  public static void Main()
                  {
                         Employee ep = new Employee();
                         HumanResource hr = new HumanResource();
                         ep.OnSalaryCompute+=new SalaryCompute(hr.SalaryHandler);       //注册
                         ep.FireEvent();        //触发事件
                         Console.Read();
                  }
           }
    }

    其实,从上面的代码看来,事件就是回调。所触发的事件,就是回调方法。

    三、事件伪造

    “事件”是指当对象发生某些事情时,向其它对象提供通知的一种方法。在C#中,事件是通过delegate来实现的。
    事件有两个角色:一是事件发送方,一是事件接收方。事件发送方是指触发事件的对象,事件接收方是指注册事件发生时被通知的对象。
    这里,我想重点说下事件伪造事件的解决方法。下面先看下什么是伪造事件。
    在一个类中我们定义一个委托,把它做公有类型。如下:

    public delegate void DelDbClick();
    public DelDbClick delDbClick;

    那么,外部的一个类,可以做一个方法,把这个委托设置成NULL(清除监听),也可以主动触发这个委托所关联的事件(假冒事件)。这种时候就可以造成“伪造事件”。
    这个时候,我们可以用事件来对它进行包装,以消除上面的这两种情况。

    public delegate void DelDbClick();
            private DelDbClick delDbClick;
    
            public event DelDbClick AddEvent
            {
                add
                {
                    delDbClick += value;
                }
                remove
                {
                    delDbClick -= value;
                }
            }

    像这样,把委托定义成私有的,然后定义一个这个委托类型的事件来把它向外开放。那么外部就既不能直接把委托设置成NULL,也不能直接调用委托了。
    当然,我们可以直接写一个方法来做这种包装,就象用属性来包装私有字段一样,但这样定起来更简单。

    四、事件与委托之间的关联

    这本来不应该单独写,但这个问题,被问得太多次了~~
    首先应该明白,委托得一种类型(书上也说是一种数据结构),它与类平级,而事件是与方法属性同级别的,事件是用委托来实现的。有人说事件是一种特殊的委托,我觉得这样理解也可以。通过上面的例子,我们也知道,事件是对委托封装,方便了事件注册(多播委托),还可以消除误操作(事件伪装)。

    五、为会么线程交叉通信要用委托

    这是我一次面试的时候,被问到的。当时,面试官问我对于多线程了解多少。我确实不清楚,但也不能一点也不说。
    说到时交叉通信要用委托时,面试官问:为什么要用委托,一下子懵了。
    我现在的理解是:交叉通信的时候,用到了一个名为“invok”的方法,而这个方法中要求提供一个委托过去。其实,这个地方就是一个回调。那么,为什么用委托就清楚了。因为在C#中不能向C/C++一样,直接把方法作为参数传递。因为C/C++中传递方法名是一个指针,C++中的指针不通过MSIL而是直接和内存打交道,这便是指针不安全的原因所在。而委托则不同。C#中的委托不与内存打交道,而是把这一工作交给CLR去完成成。CLR无法阻止将不安全的代码调用到本机(非托管)代码中或执行恶意操作。然而当代码的类型安全时,CLR的安全性强制机制将确保代码不会访问本机代码,除非它有访问本机代码的权限。此外,委托作为参数进行传递的时候,传递的信息包括参数个数,参数类型,返回值类型等。因此委托是类型安全 。
    我觉得这也就是为什么要用委托的原因了。

  • 相关阅读:
    7种jvm垃圾回收器,这次全部搞懂
    3分钟学会redis主从复制搭建,及原理
    一文搞懂什么是递归,程序员必会算法之一
    Redis持久化方式:RDB和AOF详解,对比
    jvm垃圾回收算法详解,看这一篇就够了(求点赞)
    Redis命令大全,满足你的日常工作,看这一篇就够了(求点赞)
    Java自定义异常为什么性能差(求点赞)阿里双十一的性能凶手之一
    jvm类加载器,类加载机制详解,看这一篇就够了(求点赞)
    show processlist 命令详解,MySQL优化看这一篇就够了
    asp.net中的post和get请求操作
  • 原文地址:https://www.cnblogs.com/La5DotNet/p/2820701.html
Copyright © 2020-2023  润新知