委托和事件(一)

这篇博文我不讲委托和事件的概念,因为大段的文字概念没有任何意义。

具体想了解,委托和事件的概念可以MSDN查阅。

我这篇文章的主题思路是委托如何一步步进化成事件:

何为委托--->委托来实现事件--->用方法对委托的封装--->Event的

add,remove方法的引入--->标准事件写法--->反编译探究竟。

用几个例子以及Reflector反编译探究委托和事件的关系。不足之处,还望多多指教...

何为委托:

首先,委托是一种类型,是一种定义了方法签名的类型。

委托可以理解为函数指针(安全),并且委托约束了方法的签名(由返回类型和参数组成),

所以实例化委托时,可以将其实例与任何具有相同签名(由返回类型和参数组成)得方法相关联,

如果按照C语言函数指针理解,即委托实例指向某个方法。

为什么要用委托:

举个简单的例子:

例如,需要判断一个数是为奇数还是偶数?

可能我们会这样实现:

     static void Main(string[] args)
        {
            Console.WriteLine((4 % 2 == 0) ? "偶数" : "奇数");
            Console.WriteLine((5 % 2 == 0) ? "偶数" : "奇数");
            Console.ReadKey();
        }

上面例子很简单,但是很不灵活,我们稍加改进:

static void Main(string[] args)
{
     Console.WriteLine("请输入一个数字:");
     int i = int.Parse(Console.ReadLine());
     Console.WriteLine((i%2==0)?"偶数":"奇数");
     Console.ReadKey();
}

上面这个简单的例子,也是挺有玄机的。对于程序员,我们不关心客户端用户传过来是奇数还是偶数,况且我们也不知道传过来的参数到底是多少,

我们只关心怎样来实现功能。对于用户来说,他们不必关心底层到底是怎样实现功能的,他们只负责输入数字即可“坐享其成”。这个例子,

可以理解成一个最简单的解耦。

  看了上面这个例子,我们再举一个例子来演示委托怎么替做什么:

  

    //委托是一种定义方法签名的类型。 当实例化委托时,可以将其实例与任何具有兼容签名的方法相关联。 可以通过委托实例调用方法。
    //一个委托声明:
    public delegate void ChangeDelegate(int i);
    class Program
    {
        static void Main(string[] args)
        {
            ChangeDelegate c = null;
            if (DateTime.Now.Second%2==0)
            {
                c = Even;//委托可以理解为函数指针(安全),并且委托类型ChangeDelegate约束了参数类型,所以c可以指向Even方法
                c(DateTime.Now.Second);
            }
            else
            {
                //c = Odd;
                c = new ChangeDelegate(Odd);//标准写法
                c(DateTime.Now.Second);
            }
            Console.ReadKey();

        }
        static void Even(int i)
        {
            Console.WriteLine("{0}是偶数",i);
        }
        static void Odd(int i)
        {
            Console.WriteLine("{0}是奇数",i);

        }
    }

上面代码可以看出,程序员并不知道委托实例到底指向那个函数,但他可以确定,指向的那个方法必定是受ChangeDelegate约束的。

再看一个例子,我们要对数组进行操作:

 class Program
    {
        static void Main(string[] args)
        {
            int[] arr = { 1,2,3,4,5,6,7,8,9};
            List<int> newList=Filter(arr,IsOdd);
            Console.WriteLine(string.Join("-",newList.ToArray()));
            Console.ReadKey();

        }
        /// <summary>
        /// 判断是否为偶数
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        static bool IsEven(int i)
        {
            return i%2==0;
        }
        /// <summary>
        /// 判断是否为奇数
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        static bool IsOdd(int i)
        {
            return i%2==1;

        }

        static List<int> Filter(int[] value, FilterDelegate f)
        {
            List<int> list=new List<int> ();
            foreach (var item in value)
            {
                if (f(item))
                {
                    list.Add(item);
                }
            }
            return list;
        }
    }
    delegate bool FilterDelegate(int i);

所以,static List<int> Filter(int[] value, FilterDelegate f),这儿只需要传过来一个方法,

而我们程序员并不关心这是个什么方法,能替我们做什么,这就是委托的好处。

  事件和委托的联系:学习事件之前,先来用委托来模拟实现事件:

 

    class Program
    {
        static void Main(string[] args)
        {
            Counter c = new Counter();
            c.onCount = Count;//相当于订阅了一个事件
            c.onCount += Count_2;//可以多个人同时监听了
            int j = 0;
            while (j<=100)
            {
                j++;
                c.Next();
            }
            Console.ReadKey();
        }
        static void Count(int value)
        {
            Console.WriteLine("Count监听了{0}是偶数", value);
        }
        static void Count_2(int value)
        {
            Console.WriteLine("Count_2监听了{0}是偶数", value);
        }
    }
    class Counter
    {
        public OnCountDelegate onCount;//把委托对象声明为一个字段
        private int i = 0;
        public void Next()
        {
            i++;
            if (i%2==0)
            {
                if (onCount!=null)
                {
                    //解耦:解除耦合。
                    //不用关心到底指向谁,调用就行
                    onCount(i);//触发事件,调用onCount指向的函数,相当于把事件通知出去
                }
            }
        }
    }
    public delegate void OnCountDelegate(int value);
  c.onCount = Count;
  c.onCount += Count_2; 相当于监听事件,触发事件时,只要哦调用onCount指向的函数,这样相当于把事件通知出去。所以上面这个Demo之后,我们再可以对委托来实现事件进行扩展: 我们自定义一个双击火箭手按钮[用户控件]: 
namespace 双击火箭手按钮
{
    //声明一个委托
    public delegate void DoubleClickDelegate();
    public partial class DoubleClickButton : UserControl
    {
        public DoubleClickButton()
        {
            InitializeComponent();
        }

        private int i;
        //把一个的委托对象定义为Public字段
        public DoubleClickDelegate OnDoubleClick;

        private void button1_Click(object sender, EventArgs e)
        {

            i++;
            if (i==2)
            {
                i = 0;
                if (OnDoubleClick!=null)
                {
                    OnDoubleClick();//调用事件的响应函数
                }
            }
        }
    }
}

然后我们在Form1中托入一个我们自定义的DoubleClickButton:

这儿我们可以仿照普通Button按钮监听Click事件:  this.button1.Click += new System.EventHandler(this.button1_Click);来那样做:对DoubleClickButton的

OnDoubleClick事件进行监听:
        private void Form1_Load(object sender, EventArgs e)
        {
            doubleClickButton1.OnDoubleClick = doFire;
            doubleClickButton1.OnDoubleClick += doIce;        }
        void doFire()
        {
            MessageBox.Show("双击火枪手开火");
        }
           void doIce()
        {
            MessageBox.Show("双击火枪手下冰雨");
        }
这样一个简单的用户控件就完成了,双击两下触发了OnDoubleClick事件,并且去执行相关联的响应函数(doFire,doIce)。

接着我们在对这个程序进行修改,来干扰我们的双击火枪手按钮:

我们在捣乱这个按钮的中写入:

        private void button1_Click(object sender, EventArgs e)
        {
            doubleClickButton1.OnDoubleClick = null;
        }
上面的操作意味着我们把委托变量指向了NULL,这就破坏了之前我们的监听事件。再比如:在模拟执行这个按钮中写入:
        private void button2_Click(object sender, EventArgs e)
        {
            doubleClickButton1.OnDoubleClick();
        }

上面代码模拟执行了双击火枪后按钮,本来需要双击两下才能触发事件,而这儿可以直接去执行事件的响应函数。

所以为了防止外界对我的事件的干扰,我们把

public OnCountDelegate onCount;//把委托对象声明为一个字段

改为:

   //把委托申明为Private,防止外界直接=NULL或者OnDoubleClick()仿照事件
   private DoubleClickDelegate OnDoubleClick;

再对私有的委托用一个AddDoubleClick进行对外界的过滤,所以完整代码应该是这样的:

 //把委托申明为Private,防止外界直接=NULL或者OnDoubleClick()仿照事件
        private DoubleClickDelegate _OnDoubleClick;
        public void AddDoubleClick(DoubleClickDelegate d)
        {
            _OnDoubleClick += d;
        }

        private void button1_Click(object sender, EventArgs e)
        {

            i++;
            if (i==2)
            {
                i = 0;
                if (_OnDoubleClick!=null)
                {
                    _OnDoubleClick();//调用事件的响应函数
                }
            }
        }

上面这样处理之后,我们的控件就不会被外界干扰了。

但是这样操作太复杂,所以微软为我们提供了更为简便的方式既 Event:

对上面的双击火枪手按钮在做稍微修改:

namespace 三击暴走按钮
{
    public delegate void RampageDelegate();
    public partial class RampageThreeClickButton : UserControl
    {

        //定义一个私有的委托类型字段
        private RampageDelegate onRampage;
        //类似于属性那样对委托进行了封装
        public event RampageDelegate OnRampage
        {
            add { onRampage += value; }
            remove { onRampage -= value; }

        }
        private int count;
        public RampageThreeClickButton()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            count++;
            if (count==3)
            {
                if (onRampage!=null)
                {
                    onRampage();
                }
                count = 0;
            }
        }
    }
}

再将定义好的暴走控件拖到Form1中:

后台代码是这样的:

       public Form1()
        {
            InitializeComponent();
        }

        private void rampageThreeClickButton1_Load(object sender, EventArgs e)
        {
            //对事件进行注册,即监听事件,只能用+=,不用用=,这样防止了外界=NULL干扰
            rampageThreeClickButton1.OnRampage += doFire;
            //同理
            rampageThreeClickButton1.OnRampage += new RampageDelegate(doThunder);
            rampageThreeClickButton1.OnRampage += new RampageDelegate(doIce);

        }
        void doFire()
        {
            MessageBox.Show("烈焰之怒");
        }
        void doThunder()
        {
            MessageBox.Show("上古雷霆");
        }
        void doIce()
        {
            MessageBox.Show("极地冰雨");
        }

现在还能干扰吗?当然不行:

OK,到目前为止,大家对委托和事件肯定有一个深刻的认识:

我们对事件进行反编译之后,相信一切疑问皆是浮云:

我们举一个标准的事件案例:

namespace DoubleKissinUButton
{
    public delegate void KissinUDelegate();//委托别忘记
    public partial class KissinUButton : UserControl
    {
        public event KissinUDelegate onKissinU;//用Event关键字来申明一个事件
        public KissinUButton()
        {
            InitializeComponent();
        }
        private int i = 0;

        private void button1_Click(object sender, EventArgs e)
        {
            i++;
            if (i==2)
            {
                if (onKissinU!=null)
                {
                    onKissinU();
                }
            }
        }
    }
}

界面:

 public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            kissinUButton1.onKissinU += new KissinUDelegate(kissinUButton1_onKissinU);
        }

        void kissinUButton1_onKissinU()
        {
            MessageBox.Show("点我我就吻吻你");
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }

通过反编译看看:

点击进入看看:

委托和事件没有可比性,因为委托是类型,事件是对象,上面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别。事件的内部是用委托实现的。 因为对于事件来讲,外部只能“注册自己+=、注销自己-=”,外界不可以注销其他的注册者,外界不可以主动触发事件,因此如果用Delegate就没法进行上面的控制,因此诞生了事件这种语法。add、remove。

事件是用来阉割委托实例的。事件只能add、remove自己,不能赋值。事件只能+=、-=,不能=、不能外部触发事件。

时间: 2024-12-19 21:57:27

委托和事件(一)的相关文章

C#高级知识点概要(1) - 委托和事件

作者:linybo 要成为大牛,必然要有扎实的基本功,不然时间再长项目再多也很难有大的提升.本系列讲的C# 高级知识点,是非常值得去撑握的,不仅可以让你写代码时游刃有余,而且去研究和学习一些开源项目时,也不会显得那么吃力了. 希望大家记住,这里讲的所有的知识点,不仅仅是了解了就可以了,还要会灵活用,一定要多思考,撑握其中的编程思想. 本文讲的是委托和事件,这两个词可能你早就耳熟能详,但你是否真正撑握了呢? 本系列讲的C#高级知识点都是要求开发时能达到可以徒手写出来的水平(不依赖搜索引擎.找笔记等

委托与事件

委托在底层就是一个函数的指针,委托是事件的基础. 你可以传递引用类型.值类型.但是你有没有需要传一个方法呢?传方法的过程就是委托. 消息类: public class Message { /// <summary> /// 传引用类型 /// </summary> /// <param name="msg"></param> public static void Send(string msg) { Console.WriteLine(&

C#学习(一):委托和事件

预备知识 在学习委托和事件之前,我们需要知道的是,很多程序都有一个共同的需求,即当一个特定的程序事件发生时,程序的其他部分可以得到该事件已经发生的通知. 而发布者/订阅者模式可以满足这种需求.简单来说,在这种模式中,发布者定义了一系列程序的其他部分可能感兴趣的事件.其他类可以"注册",以便再这些事件发生时发布者可以通知它们.这些订阅者类通过向发布者提供一个方法来"注册"以获取通知.当事件发生时,发布者"触发事件",然后执行订阅者提交的所有事件.

C#中委托和事件

目 录 1.1 理解委托 2 1.1.1 将方法作为方法的参数 2 1.1.2 将方法绑定到委托 4 1.2 事件的由来 6 1.2.1 更好的封装性 6 1.2.2 限制类型能力 9 1.3 委托的编译代码 10 1.4 .NET 框架中的委托和事件 11 1.4.1 范例说明 11 1.4.2 Observer 设计模式简介 12 1.4.3 实现范例的Observer 设计模式 13 1.4.4 .NET 框架中的委托与事件 14 1.5 委托进阶 16 1.5.1 为什么委托定义的返回值

C#委托和事件定义和使用

委托 定义委托的语法和定义方法比较相似,只是比方法多了一个关键字delegate ,我们都知道方法就是将类型参数化,所谓的类型参数化就是说该方法接受一个参数,而该参数是某种类型的参数,比如int.string等等:而委托是将方 法参数化,说了上面的那个类型参数化之后,相信你也能猜到方法参数化的意思了,对,就是将方法作为一个参数传到一个委托中. 首先来看看声明委托的语句: public deletate void MyDelegate(); public:访问修饰符  delegate:关键字 

[转载]C#深入分析委托与事件

原文出处: 作者:风尘浪子 原文链接:http://www.cnblogs.com/leslies2/archive/2012/03/22/2389318.html 同类链接:http://www.cnblogs.com/SkySoot/archive/2012/04/05/2433639.html 引言 本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单.还将为您解释委托的协变与逆变,以及如何使用 Deleg

[转载]C#委托和事件(Delegate、Event、EventHandler、EventArgs)

原文链接:http://blog.csdn.net/zwj7612356/article/details/8272520 14.1.委托 当要把方法作为实参传送给其他方法的形参时,形参需要使用委托.委托是一个类型,是一个函数指针类型,这个类型将该委托的实例化对象所能指向的函数的细节封装起来了,即规定了所能指向的函数的签名,也就是限制了所能指向的函数的参数和返回值.当实例化委托的时候,委托对象会指向某一个匹配的函数,实质就是将函数的地址赋值给了该委托的对象,然后就可以通过该委托对象来调用所指向的函

C#语法之委托和事件

从大学就开始做C#这块,也做C#几年了,最近又从ios转回.Net,继续做C#,之前也没有写博客的习惯,写博客也是从我做ios的时候开始的,现在既然又做回了.net,那就写点关于.Net的博客,可能在大牛眼里这些都是简单基础的,不过回过头看我当时初学的时候觉得委托事件是不容易理解的,我这里也是想着联系着OC,两者有比较的学习下.毕竟都是面向对象语言,思想是相通的. 委托在OC中类似block,都是指向一个函数,其实他没和C++的函数指针类似.但委托还是和函数指针不太一样,委托是完全面向对象的,是

C#综合揭秘——深入分析委托与事件

本篇文章将为你介绍一下 Delegate 的使用方式,逐渐揭开 C# 当中事件(Event)的由来,它能使处理委托类型的过程变得更加简单.还将为您解释委托的协变与逆变,以及如何使用 Delegate 使 Observer(观察者)模式的使用变得更加简单.在事件的介绍上,会讲述事件的使用方式,并以ASP.NET的用户控件为例子,介绍一下自定义事件的使用.最后一节,将介绍Predicate<T>.Action<T>.Func<T,TResult>多种泛型委托的使用和Lamb

C#——委托与事件

首先,来了解一下什么是委托,什么是事件,这两者之间有什么区别? 委托: (1)是一种数据类型,可以像类一样声明数据类型,声明变量 (2)只有委托才能将方法当做变量来保存 (3)不同类型的方法需要使用不同的委托类型来存储,所以不同类型的方法需要定义不同的类来存储不同的对象 (4)同一种方法可以使用同一个委托类型来存储 先通过一小段代码来了解一下 //先定义一个委托,委托也可以声明变量,还有返回值 public delegate void MyDelegate(); public delegate