• 【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事件,由于调用者只能触发事件,事件只能由被调用方来调用,所以即便调用者尝试修改事件的函数列表,但真到调用时也无法改变事件的函数列表。即函数列表始终由被调用者来维护,调用者的行为被约束了!

  • 相关阅读:
    解放双手,数据库智能调参CDBTune等你来测
    选择:成本和安全?我都要!
    腾讯云Redis全面升级,性能提升400%,可用性高达5个9
    腾讯云数据库TDSQL大咖论道 | 基础软件的过去、现在、未来
    Nginx配置http强制跳转https
    golang从实践到放弃 牧羊人
    基于 libclang 编译 C 文件的坑( error: unknown type name 'uint8_t' )
    在一个数组里过滤掉另一个数组
    接口自动化测试小结
    批处理(BAT)读取目录下所有目录并写入变量中
  • 原文地址:https://www.cnblogs.com/guxin/p/csharp-how-to-use-event-and-delegate.html
Copyright © 2020-2023  润新知