【C#】事件(Event)和代理/委托(Delegate)

代理(Delegate)的例子

delegate void MyDelegate(string str,int index);    // 声明代理

class Test 
{
    public static void Show(string str, int index) // 声明方法
    {
        Console.WriteLine("Show"+str+index.ToString());
    }

    public static void Main(string[] args)
    {
        MyDelegate md = new MyDelegate(Show); // 1.实例化代理,传入方法
        md("hello world", 22);                // 2.传入参数
    }
}

事件结合代理的完整例子

// 事件用到的代理,以般以×××Handler的格式进行命名
private delegate void CryHandler();    // 无参代理

// 玩具小鸭的类
class Duck
{
    // 定义小鸭的唱歌事件
    public event CryHandler DuckCryEvent;

    public Duck()
    {
        // 把小鸭唱歌的事件挂接到Cry方法上
        DuckCryEvent += new CryHandler(Cry); // 注册事件,传入方法
    }

    // 小鸭唱歌事件对应的处理方法
    public void Cry()
    {
        Console.WriteLine("我是一只小鸭,呀呀呀....");
    }

    // 小鸭被摇动
    public void BeShaked() //执行方法,引发cry事件
    {
        DuckCryEvent();                       // 执行事件,传入参数
    }
}

class MyClass
{
    public static void Main3(string[] args)
    {
        // 买一只小鸭
        Duck d = new Duck();
        // 摇一摇小鸭,它就会调触发小鸭的Cry事件,小鸭就会唱歌
        d.BeShaked();
    }
}

参考:

 

事件与委托的区别?

实际上,事件是对委托的封装。如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用到新的委托对象,也就删除了当前要调用的方法列表;更糟糕的是,公共的委托成员打破了封装不仅导致代码难以维护和调试,而且会导致应用程序有安全风险。

参考:

 

怎么理解事件对函数列表的保护作用呢?看下面的例子。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Test
    {
        public delegate void MyDelegate(string str, int index);    // 代理可以声明在类的外部
        public event MyDelegate MyHandler; // 事件必须在类内部定义

        public void FireEvent()
        {
            MyHandler = Show;     // 事件的函数列表交由定义方维护,不会被调用者修改!
            MyHandler += Show;    
            MyHandler("test", 1); // 会调用两次Show()
        }

        public void Show(string str, int index)
        {
            Console.WriteLine("Show : " + str + index.ToString());
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // 使用代理
            Test t = new Test();
            Test.MyDelegate my = new Test.MyDelegate(t.Show);  // 修改参数,调用方甚至可以修改代理的函数列表
            my += DoSomething;                         // 调用方可自由修改被调用方定义的委托的函数列表!
            //my.Invoke("这是Program.DoSomething()", 2); // 运行结果:先调用t.Show()再调用DoSomething()

            // 使用事件
            t.MyHandler += DoSomething;
            // t.MyHandler("12", 1); // 报错:Test.MyHandler只能出现在 += 或 -= 的左边(从类型“Test”中使用时除外)
            // t.MyHandler.Invoke("这是Program.DoSomething()", 2); // 错误,事件不存在Invoke()调用,只会被调用方触发,再由事件定义方自行来调用
            t.FireEvent(); // 运行结果:会发现DoSomething()没有被调用,因为事件中的函数列表由被调用方维护,不会被调用方修改!
            Console.ReadLine();
        }

        public static void DoSomething(string str, int index)
        {
            Console.WriteLine("DoSomething");
        }
    }

}

可见,如果使用delegate代理,代理的函数列表会暴露给调用者,调用者可以任意修改它(只要符合代理声明的方法传参、返回值即可)!这样被调用者将无法约束调用者的行为。

而如果使用evnet事件,由于调用者只能触发事件,事件只能由被调用方来调用,所以即便调用者尝试修改事件的函数列表,但真到调用时也无法改变事件的函数列表。即函数列表始终由被调用者来维护,调用者的行为被约束了!

 

 

posted @ 2016-11-18 10:01  霍莉雪特  阅读(1012)  评论(0编辑  收藏  举报