zoukankan      html  css  js  c++  java
  • C#语法——事件,逐渐边缘化的大哥。

    事件是C#的基础之一,学好事件对于了解.NET框架大有好处。

    事件最常见的比喻就是订阅,即,如果你订阅了我的博客,那么,当我发布新博客的时候,你就会得到通知。

    而这个过程就是事件,或者说是事件运行的轨迹。

    事件是发散,以我的博客为核心,向所有订阅者发送消息。我们把这种发散称之为[多播]。

    最常见的事件用途是窗体编程,在Windows窗体应用程序和WPF应用程序中。

    当在窗体中点击按钮,移动鼠标等事件时,相应的后台程序会收到通知,再执行代码。

    事件的定义

    官方对事件的说明是这样的:类或对象可以通过事件向其他类或对象通知发生的相关事情。

    换成正常语言就是,事件可以定义成静态的或普通的,所以事件就可以由声明的对象调用,也可以直接通过类调用静态事件。

    事件是C#中的一种类型,除了框架为我们定义好的事件外,我们还可以自定义事件,用event关键字来声明。

    下面我们来看最基础的事件定义。

    public delegate void TestDelegate(string message);                                                  
    public event TestDelegate testEvent;
    

    我们首先定义了一个委托,然后利用event关键字,定义一个事件。

    整体上看,好像就是在定义一个委托,只是在委托的定义之前,加了个event关键字。

    没错,事件的定义就是这样,因为要声明一个事件,需要两个元素:

    一,标识提供对事件的响应的方法的委托。

    二,一个类,用存储事件的数据。即,事件要定义在类中。

    下面我们来为这个事件赋值。

    public void Init()
    {   
        testEvent += new TestDelegate(EventSyntax_testEvent); 
        testEvent += EventSyntax_testEvent; 
    }
    private void EventSyntax_testEvent(string message)
    {
        Console.WriteLine(message);
    }
    

    如代码所示,我们使用了+=这个符号来为事件赋值,赋值的内容是一个委托和一个函数。

    其中+=我们将他理解为【添加】。

    代码中,我们使用两种赋值模式,但实际上都是为事件testEvent添加一个委。

    第二种将函数直接【添加】到事件中,编译时也会把函数转换成委托【添加】到事件中。

    系统提供事件

    C#的框架都很经典,而每个经典框架都为我们提供了一些经典事件。

    由于事件必须[标识响应方法的委托],所以这些事件所使用的委托都有一个共同的特点,命名中包含Event。

    比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

    其中最经典的就是EventHandler和RoutedEventHandler。

    EventHandler:

    EventHandler定义如下

    [SerializableAttribute]
    [ComVisibleAttribute(true)]
    public delegate void EventHandler(
    	object sender,
    	EventArgs e
    )
    

    他包含了两个参数,即当我们为事件添加EventHandler委托后,再去触发该事件;被触发的委托将得到object sender和EventArgs e两个参数。

    sender:代表源,即触发该事件的控件。

    e:代表事件参数,即触发该事件后,事件为被触发的委托,传递了一些参数,以方便委托在处理数据时,更便捷。

    根据这个原理,我们可以分析出很多东西。

    比如,当控件DataGrid的事件被触发时,只要查看一下sender的真实类型,就可以知道,到底是DataGrid触发的事件,还是DataGridRow或DataGridCell触发的了。

    RoutedEventHandler:

    RoutedEventHandler即路由事件,他的定义如下

    public delegate void RoutedEventHandler(
    	Object sender,
    	RoutedEventArgs e
    )
    

    RoutedEventHandler也为我们提供了sender和e两个参数。

    但RoutedEventHandler特别之处是,他的sender并不一定是真实的源,因为他是一个冒泡路由事件,即上升事件。

    这里如果大家有好奇心去看官方文档,那么会在相关的介绍中看到两个单词sender和source。

    通过这两个单词,我们会清晰的了解路由事件。简单描述一下sender和source,它们一个是发送者,一个是源。

    在EventHandler中,sender即source,因为它是直接事件。而在冒泡事件中,sender不一定等于source。即发送者不一定是源。

    下面我们用WPF来看看路由事件。

    我们首先在XAML页面定义一个RadioButton按钮,然后设置他的模板是Button。然后分别定义各自的Click方法。

    Xaml页面如下:

     <RadioButton Click="btnParent_Click">
                <RadioButton.Template>
                    <ControlTemplate>
                        <StackPanel>
                            <TextBlock Text="我的名字" ></TextBlock>
                            <Button Content="Kiba518"   Click="btnClild_Click" ></Button>
                        </StackPanel>
                    </ControlTemplate>
                </RadioButton.Template> 
    </RadioButton> 
    

    cs文件事件如下:

     private void btnParent_Click(object sender, RoutedEventArgs e)
     {
         string type = sender.GetType().ToString();//RadioButton
     }
    
     private void btnClild_Click(object sender, RoutedEventArgs e)
     {
         string type = sender.GetType().ToString();//Button
     }
    

    运行起来,我们点击按钮,通过断点我们可以看到,我们点击的按钮触发了btnClild_Click和btnParent_Click事件

    顺序是先btnClild_Click后btnParent_Click。

    通过获取sender的类型,我也可以看到,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

    事件驱动编程

    事件驱动编程这个概念给我的感觉很怪,因为一直用C#,而C#的很多框架都是事件驱动的,所以一直觉得事件驱动是理所当然。

    而当事件驱动设计这个词经常出现后,反而感觉怪怪的。

    就好像,天天吃大米饭,突然有一天,所有人都说大米饭好香的感觉一样,你一听就感觉怪怪的。

    因为事件驱动对于C#开发而言,实在太普通了。当然,这也得益于微软框架做的实在是太好了。

    所以,我也不知道如何在C#里讲事件驱动编程。因为使用C#的框架就是使用事件驱动编程。

    事件和委托到底是什么关系?

    事件是用来多播的,并且用委托来为事件赋值,可以说,事件是基于委托来实现的。

    但委托中也有多播,那为什么要单独弄出来一个事件呢?

    首先,存在即合理,事件一定有他存在的意义。 

    事件存在的意义

    我对事件存在的意义是这样理解的。我们在C#编写框架时,几乎不用委托的多播,因为委托的多播和事件存在严重的二义性。虽然编写框架的人学会了使用委托的多播,但使用框架的同事可能并还不太熟练,而且C#框架中,大多是使用事件来进行多播的。

    所以委托的多播和事件一起使用的框架,会造成使用这个框架的初级开发者很多困惑,而这种困惑,会产生很多不必要的问题。

    比如, 你定义了一个委托,另一个开发者用这个委托做了个多播,当第三个开发者来维护这段代码时,如果他是新手,不了解委托的多播,那就很有可能只修改了委托调用的代码。而没有去同步多播这个委托的代码。那系统就产生了隐藏的bug。

    那么,事件和委托到底是什么关系呢?

    事件与委托的确存在千丝万缕的关系,怎么讲都是正确的。但,C#开发者只需要记住,他们俩没关系即可。在C#事件是事件,委托是委托。两者就如同int和string一样,没有任何关系。

    原因很简单,学习的过程中尽量降低概念混淆。而且,在C#开发中,好的架构者也通常会将事件和委托分离,所以,就认为事件和委托没有关系即可。

    结语

    其实事件很好理解,一点不复杂。我在写这篇文章的过程中,也没想到什么特别的或者说比较高级的用法。

    但真实的应用场景中,我的感觉是,随着MVVM的成长,事件其实在被逐渐抛弃。虽然微软做了很多经典的事件驱动框架。但那都是过去了。

    比如WPF虽然支持事件驱动,但MVVM在WPF下的表现堪称完美,所以WPF下的事件几乎没有人用了。

    再比如前端的Angularjs等框架,提供了优质的MVVM使用效果,也让新的前端设计师逐渐放弃了事件。

    所以,事件在未来的编程中,很可能将不在有那么重要的地位了。但学好事件,对于我们理解微软框架,还是有很大帮助的。

    C#语法——元组类型

    C#语法——泛型的多种应用

    C#语法——await与async的正确打开方式

    C#语法——委托,架构的血液

    我对C#的认知。

    ----------------------------------------------------------------------------------------------------

    注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
    若您觉得这篇文章还不错,请点击下方的推荐】,非常感谢!

     

  • 相关阅读:
    Java 动态代理机制分析及扩展
    记:从百度空间搬家到博客园写博客要写的舒服
    字符串与byte[]之间的转换
    关于中文的几个编码GB2312、GBK、GB18030、GB13000
    深入biztalk中各种端口绑定方式(七) 直接绑定之Partner Orchestration
    X.509 数字证书结构和实例
    深入biztalk中各种端口绑定方式(六) 直接绑定之Self Correlating
    在代码中使用biztalk类库建立Message和Part
    Byte[]和BASE64之间的转换
    深入biztalk中各种端口绑定方式(五) 直接绑定之MessageBox
  • 原文地址:https://www.cnblogs.com/kiba/p/9436431.html
Copyright © 2011-2022 走看看