• C# 事件和委托的用途及区别


    ▲简单委托的构成:

    可以选择将委托类型看做只定义了一个方法的接口,将委托的实例看做实现了那个接口的一个对象。

    1. 声明委托类型——定义委托

    混乱的根源:容易产生歧义的“委托”
    委托经常被人误解,这是由于大家喜欢用委托这个词来描述委托类型和委托实例。
    Console.WriteLine("StringProcessor父类:" + typeof(StringProcessor).BaseType);//StringProcessor父类:System.MulticastDelegate

    委托的基类:System.MulticastDelegate——的基类:System.Delegate

    2. 为委托实例的操作找到一个恰当的方法

    3、创建委托实例

    需要一个方法以及(对于实例方法来说)调用方法的目标;

    单纯创建一个委托实例却不在某一时刻调用它是没有什么意义的。看看最后一步——调用。

    4. 调用委托实例

    “委托实例被调用”中的“调用”对应的是invoke。
    invoke理解为“唤出”某个东西来帮你调用一个信息不明的方法时,用invoke比call恰当。理解为唤出和调用区别不明显。
    调用委托实例的两种方式——显式调用Invoke和使用C#的简化形式。一般情况下只需使用简化形式。

    1. 声明的委托实例.invoke(参数)
    2. 声明的委托实例(参数)

    委托的实质是间接完成某种操作,这增大了复杂性(看看为了输出这点儿内容,用了多少行代码),但同时也增加了灵活性。

    5.合并和删除委托

    System.Delegate类型的静态方法Combine和Remove负责创建新的委托实例。
    其中, Combine等价于+=,而Remove等价于-=

    委托是不易变的:

    委托实例类似于string,创建了委托实例后,有关他的一切就不能改变。

     

    事件和委托的用途

    委托delegate:

    用途最广的是,使用委托为形参,传递实参(回调函数)时,可以使用匿名函数。对比Java:java使用匿名函数是形参是接口,创建一个接口实现类匿名类,匿名类实现接口方法。

    例如:

    var t4 = Task.Run(() => TaskMethod.DoTask("using Run method"));
    //系统方法Run 就使用了委托作为参数
    public static Task<TResult> Run<[NullableAttribute(2)] TResult>(Func<TResult> function);

    事件event:

    开发者经常将事件和委托实例,或者将事件和委托类型的字段混为一谈。之所以产生混淆,原因和以前相同,因为C#提供了一种简写方式,允许使用字段风格的事件field-like event

    字段风格的事件使所有这些的实现变得更易阅读,只需一个声明就可以了。 编译器会将声明转换成一个具有默认add/remove实现的事件和一个私有委托类型的字段

    事件不是委托实例——只是成对的add/remove方法(类似于属性的取值方法/赋值方法)事件包含一个私有委托类型字段。

    void OnEventRaised(object sender, EventArgs args);

     

    发布器类:

    • 声明委托,事件委托一般命名为:NameEventHandler
    • 声明事件
    //event关键字代表事件,返回类型为委托;
    public event BoilerLogHandler BoilerEventLog;//基于委托定义事件,委托的函数指针。
    • 创建引发事件的方法,一般命名为:OnEventName。

    订阅器类

    • 实例化发布器类
    • 绑定事件:+= 委托(或函数都可以)

    发布器类一个方法OnEventName() 等价于,订阅事件中的所有绑定方法一起执行。(广播)

    public class Host
    {
    //定义委托原型
        public delegate void OpenDoorEventHandler();
    //定义委托类型的事件
        public event GoHomeEventHandler OpenDoor;
    //定义内部一个方法,在这个方法内判断,OpenDoor事件是否被其他对象注册,一旦注册了,则调用执行事件。
        protected void OnOpenDoor()
        {
            if(OpenDoor!=null)
            {
                OpenDoor();
            }
        }
        public void GoHome()
        {
            OnOpenDoor();
        }
    }
    public class Cat
    {
        public void Run()
        {
            //猫跑了
        }
    }
    public class Dog
    {
        public void Bark()
        {
            //狗叫了
        }
    }
    如何使用如下:
    Host 主人 =new Host();
    Cat 猫 = new Cat();
    Dog 狗 = new Dog();
    
    主人.OpenDoor += new 主人.OpenDoorEventHandler(猫.Run);
    主人.OpenDoor += new 主人.OpenDoorEventHandler(狗.Back);
    
    主人.GoHome【发布器类】就等价于
    猫.Run();//【订阅器类】
    狗.Back();//【订阅器类】
    被观察者【发布器类】
    ,做出某一特定“动作”(被观察者【发布器类】的特定“动作”【发布器类中引发事件的方法,一般命名为:OnEventName】,注册了N个不同对象的不同反映【订阅器类】),观察者【订阅器类】对这个特定“动作”做出不懂的反映。
     

    2.2 事件用法三步曲

    事件机制的使用方法可以归纳为3个步骤:

    (1)事件发布者定义event以及事件相关信息  (2)事件侦听者订阅event   (3)事件发布者触发event,自动调用订阅者的事件处理方法。

    到这里可能有的小伙伴觉得 和 多播委托 一样,添加也是+=,取消也是-=

    其实事件就是多播委托的一种封装,如果不使用事件,直接使用委托,能不能实现事件机制呢?答案是完全可以。

    那么,为什么还要用事件呢?事件是怎么封装了委托呢?

    实际上,在以上案例中,使用 event 关键字在一行上定义事件是C# 提供的事件的简化记法。在我们定义了上述事件后,编译器会自动生成如下代码:

    private EventHandler<CarInfoEventArgs> newCarEvent;
    public event EventHandler<CarInfoEventArgs> NewCarEvent
    {
        add
        {
            newCarEvent += value;
        }
        remove
        {
            newCarEvent -= value;
        }
    }


    这非常像字段及其属性的关系。注意到,委托字段 newCarEvent 是私有的,因此在外部不能直接为事件赋值,但可以通过公开的 += 和 -= 运算符为事件添加实例方法。 另外,事件 event 是一种数据类型,是一个已经声明的委托类,只能在某个类的内部声明,并且只能被该类调用,不能在命名空间中声明和使用。这一点与委托 delegate 的声明不同。

    总结:事件与委托的区别在于两点:

    (1)委托是一个类,可以在命名空间中声明;而事件只能在事件发布者内部定义,且只能被该类调用。

    (2)可以直接使用一个方法为委托赋值,而事件只开放了 += 和 -= 运算符为其添加或删除方法。
    ————————————————
    版权声明:本文为CSDN博主「wnvalentin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/wnvalentin/article/details/82254656

    源码,是痛苦的,又是快乐的,如果没有这痛苦,也就没有了这快乐!
  • 相关阅读:
    QtQt Creator 操作快捷键大全
    go编译报错https://proxy.golang.org/github.com/ i/o timeout
    常用坐标系
    10 种CORS跨域解决方案
    centos问题解决方案汇总
    org.apache.axis.AxisFault: 服务器无法处理请求。 > 值不能为空。
    PHP 开启 curl_init
    帝国CMS 标签 showclasstemp
    帝国CMS 标签 listsonclass
    PHP 获取服务器 MAC 物理网卡地址
  • 原文地址:https://www.cnblogs.com/erlongxizhu-03/p/12511275.html
Copyright © 2020-2023  润新知