事件是C#的基本功之一,学好事件对于了然.NET框架大有实益。

  假诺对事件一点都不打听依旧是心猿意马的话,提议先去看张子阳的寄托与事件的稿子(相比较长,大概看完了,也忘记看这一篇了,没事,笔者会原谅你的),废话不多说,发轫进入正题。本记录不是记录古板的风浪(CLSportage事件),而是记录WPF中常用的轩然大波——路由事件,由于路由事件“传播”的时刻是顺着可视树传播的,所以在记录初阶以前还是通晓一下逻辑树和可视树。

[转载]C#寄托和事件(Delegate、伊芙nt、伊芙ntHandler、伊芙ntArgs),

一、了解C#中的预订义事件处理机制

事件最广泛的比方正是订阅,即,假使您订阅了自笔者的博客,那么,当自家公布新博客的时候,你就会收获文告。

 ① 、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree)和可视树(Visual
Tree),XAML是表述WPF的一棵树。逻辑树完全是由布局组件和控件构成。借使大家把逻辑树延伸至Template组件级别,大家就获取了可视树,所以可视树把树分的更周到。由于本记录重在记录事件,所以不做过多公布逻辑树和可视树的始末。关于逻辑树和可视树的区分可以参见。

初稿链接:

    
在写代码前大家先来熟练.net框架四之日事件有关的类和寄托,驾驭C#中预订义事件的处理。

而以此历程正是事件,大概说是事件运转的轨道。

二 、路由事件

14.1、委托

当要把措施作为实参传送给此外措施的形参时,形参需求采纳委托。委托是一个门类,是3个函数指针类型,这几个种类将该信托的实例化对象所能指向的函数的细节封装起来了,即明确了所能指向的函数的签名,也正是限量了所能指向的函数的参数和再次回到值。当实例化委托的时候,委托对象会指向某三个同盟的函数,实质正是将函数的地方赋值给了该信托的指标,然后就足以由此该信托对象来调用所针对的函数了。利用委托,程序员能够在信托对象中封装三个艺术的引用,然后委托对象作为形参将被传给调用了被引述方法的代码,而不要求了解在编写翻译时刻具体是哪个方法被调用。

相似的调用函数,我们都不会去行使委托,因为假若只是可是的调用函数,使用委托更麻烦一些;可是假设想将函数作为实参,传递给有些函数的形参,那么形参就必将要使用委托来收取实参,一般选取办法是:在函数外面定义委托对象,并针对性某些函数,再将以此目的赋值给函数的形参,形参也是该委托类型的目的变量,函数里面再通过形参来调用所指向的函数。

     EventArgs是包罗事件数量的类的基类,用于传递事件的底细。

事件是分散,以自个儿的博客为骨干,向具有订阅者发送新闻。大家把那种分散称之为[多播]。

2.壹 、小记事件

  假使看完了信托与事件的稿子,相信会对事件有更进一步的认识了,但要么要把某些基础的地点再记录一下。二个风浪要有上面多少个成分,才会变的有含义:

    • 事件的拥有者(sender)——即音讯的发送者。
    • 事件发送的音讯(伊夫ntAgs)
    • 事件的响应者——新闻的接收者(对象)。
    • 慢慢边缘化的堂弟,深入浅出WPF。响应者的计算机——新闻的接受者要对信息作出处理的方法(方法名)。
    • 响应者对发送者事件的订阅

14.1.① 、定义委托

语法如下

delegate  result-type   Identifier ([parameters]);

说明:

result-type:重临值的品种,和方法的归来值类型一致

Identifier:委托的名号

parameters:参数,要引用的法子带的参数

小结

当定义了信托随后,该信托的靶子自然能够而且也只可以指向该信托所界定的函数。即参数的个数、类型、顺序都要协作,再次回到值的档次也要同盟。

因为定义委托也就是是定义1个新类,所以能够在定义类的其它地点定义委托,既能够在三个类的里边定义,那么此时将要通过此类的类名来调用那一个委托(委托必须是public、internal),也足以在其他类的外部定义,那么此时在命名空间中与类的级别是平等的。依照定义的可知性,能够在信托定义上添加一般的访问修饰符:当委托定义在类的外界,那么能够增进public、internal修饰符;要是委托定义到类的里边,那么能够添加public、 private、 protected、internal。一般委托都以概念在类的外场的。

     EventHandler是1个委托表明如下

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

2.2 初试路由事件

  大家成立一个winform项目,然后在窗体上添加1个按钮,双击添加一个总括机,会发觉private
void btn_Click(object sender, 伊夫ntArgs
e)处理器要拍卖音讯是伊芙ntArgs类型的,那里对应的是观念的事件(大家叫它CL景逸SUV事件)。同样大家创造三个WPF项目,然后添加3个按钮,双击添加二个处理器,会意识private
void Button_Click(object sender, Routed伊夫ntArgs
e)处理器要拍卖的音信是Routed伊芙ntArgs类型的,那里对应的是路由事件。两者有哪些界别吗。让大家先看看CLRubicon事件的流弊,就像(this.btn.Click
+= new
System.伊夫ntHandler(this.btn_Click);)每贰个音讯都以从发送到响应的2个进程,当3个处理器要用多次,必须树立显式的点对点订阅关系(窗体对按钮事件的订阅,假使是再有多少个按钮的话,就要再来三遍订阅);还有二个弊病是:事件的宿主必须能够间接待上访问事件的响应者,否则不能树立订阅关系(如有三个零部件,点击组件一的按钮,想让组件二响应事件,那么就让组件二向组件一的按钮暴光一个可以访问的风波,那样一旦再多多少个嵌套,汇合世事件链,有暴露即使揭露不当就存在着威吓)。路由事件除了能很好的消除地点的标题,还有三个是路由事件在有路的情景下,能很好的根据明显的措施传播事件,因为XAML的树状结构,构成了一条条的征途,所以在WPF中,引入了路由事件。举个例子:若是窗体要以相同的情势处理四个按钮的事件,我们就足以用一句代码就化解了,this.AddHandler(Button.Click伊芙nt,
new
Routed伊芙ntHandler(this.ButtonClicked));这样就减弱代码量。下边通过三个事例初试一下路由事件。 给出XAML代码:

<Window x:Class="Chapter_06.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="GridRoot" Background="Lime">
        <Grid x:Name="gridA" Margin="10" Background="Blue">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Canvas x:Name="canvasLeft" Grid.Column="0" Background="Red" Margin="10">
                <Button x:Name="buttonLeft" Content="left" Width="40" Height="100" Margin="10"/>
            </Canvas>
            <Canvas x:Name="canvasRight" Grid.Column="1" Background="Yellow" Margin="10">
                <Button x:Name="buttonRight" Content="right" Width="40" Height="100" Margin="10" />
            </Canvas>
        </Grid>
    </Grid>
</Window>

  大家点击按钮时,无论是buttonLeft依旧buttonRight单击都能突显按钮的名目。七个按钮到顶部的window有唯一条路,左侧的按钮对应的路:buttonLeft->canvasLeft->gridA->GridRoot->Window,左边按钮对应的路:buttonRight->canvasRight->gridA->GridRoot->Window。假设GridRoot订阅多少个电脑,那么处理器应该是同等的。后台代码为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Chapter_06
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.GridRoot.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.ButtonClicked));
        }
        private void ButtonClicked(object sender, RoutedEventArgs e)
        {
            MessageBox.Show((e.OriginalSource as FrameworkElement).Name);
        }
    }
}

  上面先解释一下路由事件是怎么沿着可视树来传播的,当Button被点击,Button就起来发送新闻了,可视树上的成分假若订阅了Button的点击事件,那么才会根据新闻来作出相应的影响,假使没有订阅的话,就无所谓它发出的新闻,当然大家还足以决定它的音讯的传播情势,是从树根到树叶传播,依然树叶向树根传播以及是平昔抵达目标传播,不仅如此,还是能够控制新闻传到有个别成分时,甘休扩散。具体的会在后头记录到。其次是this.GridRoot.AddHandler(Button.Click伊夫nt,new Routed伊夫ntHandler(this.ButtonClicked));订阅事件时,首个参数是路由事件类型,在此处用的是Button的Click伊芙nt,就好像重视属性一样,类名加上依赖属性,那里是类名加上路由事件。其余2个是e.OriginalSource与e.Source的界别。由于音讯每传一站,都要把音信交个贰个控件(此控件成为了新闻的出殡和埋葬地方),e.Source为逻辑树上的源头,要想获取原始发新闻的控件(可视树的源流)要用e.OriginalSource。最后证实一下,怎么在XAML中添加订阅事件。间接用<Grid
x:Name=”gridA” Margin=”10″ Background=”Blue”
Button.Click=”ButtonClicked”>在.net平台上不可能智能提示Button,因为Click是延续与ButtonBase的风云,XAML只认识含有Click事件的成分,可是要披荆斩棘的写下去才能打响。运营方面代码,点击左侧按钮,效果如图1:

 

金沙注册送58 1

图1

私下认可的路由音信里面属性有五个,如图2,能够自动转到定义看一下其品质代表的意思。

金沙注册送58 2

图2

 

14.1.贰 、实例化委托

Identifier  objectName  =  new  Identifier( functionName);

实例化委托的面目便是将有些函数的地方赋值给委托对象。在此地:

Identifier :这些是委托名字。

objectName :委托的实例化对象。

functionName:是该信托对象所针对的函数的名字。对于那个函数名要特别注意:定义那几个委托对象自然是在类中定义的,那么一旦所针对的函数也在此类中,不管该函数是静态依旧非静态的,那么就一贯写函数名字就足以了;假如函数是在别的类里面定义的public、

internal,可是只倘若静态,那么就径直用类名.函数名,假如是非静态的,那么就类的靶子名.函数名,这些函数名与该对象是有关系的,比如假使函数中冒出了this,表示的正是对当下指标的调用。

          public delegate void EventHandler( object sender , EventArgs
e )

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

 2.3自定义路由事件

  通过地方的小规模试制路由事件,应该适度由事件某个明白了,上边就记下一下什么样自定义二个路由事件。差不多分成四个步骤:1.扬言并注册路由事件,2.为路由事件添加CL奥德赛事件包装,3.开立能够激发路由事件的法门。记忆一下依靠属性,前八个步骤应该和路由事件很相像吧。上边将八个步骤分开来表达:

率先步:注解并注册路由事件       

       //***Event为路由事件名,类型为路由事件类型
       //注册事件用的是EventManager.RegisterRoutedEvent
      //第一个参数为事件名(下面的***都为同一个单词)
       //第二个参数为事件传播的策略,有三种策略:Bubble(冒泡式),Tunnel(隧道式),Direct(直达式)分别对应上面的三种青色字体的三种方式
       //第三个参数用于指定事件处理器的类型,该类型必须为委托类型,并且不能为 null。
       //第四个参数为路由事件的宿主    
       public static readonly RoutedEvent ***Event = EventManager.RegisterRoutedEvent("***", RoutingStrategy.Bubble,
                                                             typeof(***RouteEventHandler), typeof(ClassName));

第3步:为路由事件添加CL凯雷德事件包装

       /*包装事件
        *这里与传统的数据差别是把+=和-=换成了AddHandler和RemovedHandler
        */
        public event RoutedEventHandler ***
        {
            add { this.AddHandler(***Event, value); }
            remove { this.RemoveHandler(***Event, value); }
        }

其三步:创制能够刺激路由事件的法门

        /*对于控件的事件,一般是重写宿主事件对应的方法(如Button的click事件和OnClick()方法相对应):新建消息,并把消息与路由事件相关联,
         *通过调用元素的RaiseEvent方法把时间传送出去(这里与包装器的CRL事件毫不相干),在CLR事件是用Invoke方法,下面以按钮为例
         */

        protected override void OnClick()
        {
            base.OnClick();
            ***EventArgs args = new ***EventArgs(***Event, this);
            this.RaiseEvent(args);
        }

 上面大家就来完毕二个归纳的自定义路由成效,当路由飘过四个控件的时间,展现通过该控件的时间。 上面介绍的大都了,所以就一向上代码,有亟待表明的话,再叁个个诠释。

下面是XAML代码:

<Window x:Class="DefineEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:DefineEvent" 
        Title="Routed Event" x:Name="window_1" Height="350" Width="525" local:TimeButton.ReportTime="ReportTimeHandler" >
    <Grid x:Name="grid_1" local:TimeButton.ReportTime="ReportTimeHandler" >
        <Grid x:Name="grid_2" local:TimeButton.ReportTime="ReportTimeHandler"  >
            <Grid x:Name="grid_3" local:TimeButton.ReportTime="ReportTimeHandler"  >
                <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="ReportTimeHandler" >
                    <ListBox x:Name="listBox" />
                    <local:TimeButton x:Name="timeButton" Width="200" Height="80" Content="显示到达某个位置的时间" ReportTime="ReportTimeHandler"/>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>

下面是CS代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace DefineEvent
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    delegate void ReportTimeRouteEventHandler(object sender, ReportTimeEventArgs e);
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void ReportTimeHandler(object sender, ReportTimeEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            string timeStr = e.ClickTime.ToString("yyyyMMddHHmmss");
            string content = string.Format("{0}到达{1}", timeStr, element.Name);
            this.listBox.Items.Add(content);
        }
    }
    //创建消息类型,在此可以附加自己想要的信息
    public class ReportTimeEventArgs : RoutedEventArgs
    {
        public ReportTimeEventArgs(RoutedEvent routedEvent, object source) : base(routedEvent, source) { }
        public DateTime ClickTime { get; set; }
    }
   public class TimeButton : Button
    {
        //1、为元素声明并注册事件
        public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Bubble,
                                                             typeof(ReportTimeRouteEventHandler), typeof(TimeButton));

       //2、包装事件
        public event RoutedEventHandler ReportTime
        {
            add { this.AddHandler(ReportTimeEvent,value); }
            remove { this.RemoveHandler(ReportTimeEvent,value); }
        }
        //3、创建激发事件的方法
        protected override void OnClick()
        {
            base.OnClick();
            ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this);
            args.ClickTime = DateTime.Now;
            this.RaiseEvent(args);
        }
    }
}

运行单击按钮的功用为图3:

金沙注册送58 3

图3

  注意下边包车型地铁一条龙代码,在证明和定义路由事件时,第一个参数,委托的品类和处理器方法的的参数一定要一致,不然会报错,能够把上面一句中的Report提姆eRoute伊夫ntHandler换来Routed伊夫ntHandler试试,会冒出:无法从文本“ReportTimeHandler”创造“ReportTime”。”,行号为“5”,行职分为“30”,的题材,那里根本的来头正是信托的参数和Routed伊芙ntHandler的参数差别,尽管都以(sender, 
e);可是e的档次已经发出了变化,成为了ReportTime伊芙ntArgs类型的。所以在运用在此之前,声Bellamy(Bellamy)个信托就足以了。还有个办法是利用EventHandler<ReportTime伊芙ntArgs>替换Report提姆eRoute伊夫ntHandler,其实相互的用法大约,只是分裂的写法,不过是自个儿感觉到第壹种写法会更好掌握。具体有关伊芙ntHandler<ReportTime伊芙ntArgs>的含义请参考。我们一致能够选择让第贰个参数改变成其它两连串型的探访测试结果。

public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Tunnel,
                                                             typeof(ReportTimeRouteEventHandler), typeof(TimeButton));

 假如大家期待当事件传递到grid_2方面就停下了,大家能够那样做:在ReportTimeHandler函数中添加代码。

            if (element.Name == "grid_2")
                e.Handled = true;

14.1.叁 、委托测度

C#
2.0用委托猜测扩张了委托的语法。当大家必要定义委托对象并实例化委托的时候,就能够只传送函数的名号,即函数的地方:

Identifier  objectName  =  functionName;

那其中的functionName与14.1.2节中实例化委托的functionName是平等的,没什么不相同,知足下边包车型地铁条条框框。

C#编写翻译器创制的代码是相同的。编译器会用objectName检查和测试须要的嘱托项目,因而会创制Identifier委托类型的二个实例,用functionName即方法的地方传送给Identifier的构造函数。

注意:

不能够在functionName后边加括号和实参,然后把它传送给委托变量。调用方法一般会再次来到一个不能够加之委托变量的一般性对象,除非那一个办法重临的是二个极度的信托对象。综上可得:只好把相匹配的主意的地点赋予委托变量。

信托猜度可以在要求委托实例化的任哪个地点方选择,就跟定义普通的嘱托对象是同等的。委托推测也能够用来事件,因为事件基于委托(参见本章后边的始末)。

    
注意那里的参数,前者是二个对象(其实那里传递的是指标的引用,假诺是button1的click事件则sender正是button1),前面是含有事件数量的类的基类。

事件的概念

 三、总结

  本篇记录的内容固然不多,可是感觉记录的时光专门困难,首就算因为对事件的多少个组成部分还不是格外熟悉,而且在明亮路由事件时,还要先知道逻辑树与可视树。最后依旧把这一章看完了,但那个只是发端。

  文章主要记录了路由事件的在可视树上的流传以及自定义路由事件的落到实处。假使在作品有例外的视角或提议,欢迎调换! 下一篇:《长远浅出WPF》笔记——命令篇

 

14.1.④ 、匿名格局

到最近截至,要想使委托工作,方法必须已经存在。但实例化委托还有此外一种艺术:即透过匿名情势。

用匿超级模特式定义委托的语法与最近的概念并从未分别。但在实例化委托时,就有分别了。下边是3个非凡不难的控制台应用程序,表明了什么样运用匿超形式:

using System;

namespace Wrox.ProCSharp.Delegates

{

  class Program

  {

    delegate string DelegateTest(string val);

    static void Main()

    {

      string mid = “, middle part,”;

      //在点子中定义了点子

      DelegateTest  anonDel = delegate(string param)

      {

        param += mid;

        param += ” and this was added to the string.”;

        return param;

      };

      Console.WriteLine(anonDel(“Start of string”));

    }

  }

}

寄托DelegateTest在类Program中定义,它带1个字符串参数。有分其他是Main方法。在定义anonDel时,不是传递已知的艺术名,而是选用3个大约的代码块:它前面是任重先生而道远字delegate,前面是三个参数:

delegate(string param)

{

  param += mid;

  param += ” and this was added to the string.”;

  return param;

};

匿名情势的助益是收缩了要编写制定的代码。方法仅在有嘱托行使时才定义。在为事件定义委托时,这是那多少个肯定的。(本章后边研究事件。)那促进降低代码的扑朔迷离,尤其是概念了几许个事件时,代码会显得比较简单。使用匿名格局时,代码执行得不太快。编译器仍定义了多少个办法,该措施唯有3个电动钦点的名号,大家不需求知道那么些称呼。

在利用匿名格局时,必须遵守四个规则:

壹 、在匿名格局中无法动用跳转语句跳到该匿超情势的表面,反之亦然:匿名格局外部的跳转语句不可能跳到该匿名格局的中间。

② 、在匿名方式内部无法访问不安全的代码。其它,也不能够访问在匿超形式外部使用的ref和out参数。但能够使用在匿有名的模特式外部定义的别样变量。方法内部的变量、方法的参数可以随心所欲的利用。

只要须要用匿名格局多次编辑同3个功力,就毫无使用匿超方式。而编写3个点名的章程比较好,因为该办法只需编写二次,以往可透过名称引用它。

    
上边大家切磋一下Button类看看当中的风浪注解(使用WinCV工具查看),以Click事件为例。

官方对事件的印证是那般的:类或对象足以由此事件向其余类或对象布告发出的相干作业。

14.1.⑤ 、多播委托

前方使用的种种委托都只包罗一个艺术调用,调用委托的次数与调用方法的次数相同,假设要调用几个措施,就必要反复给委托赋值,然后调用这一个委托。

信托也得以分包多少个办法,那时候要向委托对象中添加多少个点子,那种委托称为多播委托,多播委托有二个方法列表,若是调用多播委托,就足以连接调用三个办法,即先实行某三个主意,等该情势执行到位之后再实践此外三个艺术,这个点子的参数都是一致的,那几个主意的执行是在叁个线程中施行的,而不是各样方法都以一个线程,最终将履行到位具有的法子。

万一应用多播委托,就应留神对同3个委托调用方法链的各种并未正式定义,调用顺序是不确定的,不必然是服从添加办法的依次来调用方法,因而应幸免编写制定依赖于以一定顺序调用方法的代码。假若要想鲜明顺序,那么只可以是单播委托,调用委托的次数与调用方法的次数相同。

多播委托的依次艺术签名最棒是重返void;不然,就只可以获得委托最终调用的二个主意的结果,而最后调用哪个方法是无能为力鲜明的。

多播委托的每二个方法都要与信托所界定的点子的重临值、参数匹配,否则就会有荒唐。

本身自个儿写代码测试,测试的结果最近都以调用顺序和插手委托的一一相同的,不过不清除有两样的时候。 

delegate result-type Identifier ([parameters]); 

          public event EventHandler Click;

换到符合规律语言正是,事件能够定义成静态的或普通的,所以事件就足以由证明的目的调用,也得以一直通过类调用静态事件。

14.1.5.一 、委托运算符 =

Identifier  objectName  =  new  Identifier( functionName);

或者

Identifier  objectName  =  functionName;

此间的“=”号表示清空 objectName 的方式列表,然后将 functionName 到场到     objectName 的方法列表中。

     那里定义了多个伊芙ntHandler类型的轩然大波Click

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

14.1.5.② 、委托运算符 +=

objectName  +=  new  Identifier( functionName1);

或者

objectName  +=  functionName1;

此处的“+=”号表示在本来的主意列表不变的情景下,将 functionName1  到场到     objectName 的法门列表中。能够在措施列表中添加多少个一律的措施,执行的时候也会实行完全体的函数,哪怕有一致的,就会频繁实施同三个艺术。

留意:objectName 必须是曾经赋值了的,不然在概念的时候一贯选用该符号:

Identifier  objectName    +=  new  Identifier( functionName1);或者

Identifier  objectName  +=  functionName1;就会报错。

    
前边的剧情都是C#在类库中曾经为大家定义好了的。上边大家来看编制程序时产生的代码。

下边大家来看最基础的风云定义。

14.1.5.三 、委托运算符 -=:

objectName  -=  new  Identifier( functionName1);

或者

objectName  -=  functionName1;

那边的“-=”号表示在 objectName 的不二法门列表中减去多个functionName1。可以在艺术列表中一再减去划一的方法,减三次只会减3个艺术,要是列表中无此格局,那么减就平素不意思,对原来列表无影响,也不会报错。

留神:objectName 必须是一度赋值了的,不然在概念的时候一向动用该符号:

Identifier  objectName    -=  new  Identifier( functionName1);或者

Identifier  objectName  -=  functionName1;就会报错。

         private void button1_Click(object sender, System.EventArgs
e)
         {
             …
         }

public delegate void TestDelegate(string message);                                                  
public event TestDelegate testEvent;
14.1.5.④ 、委托运算符 +、-:

Identifier  objectName  =  objectName  + functionName1 –
functionName1;或者

Identifier  objectName  =  new  Identifier( functionName1) +
functionName1 – functionName1;

对于那种+、-表明式,在率先个标志+恐怕-的前方必须是寄托而不能够是方法,前边的+、-左右都不管。其一不是相对规律,还有待进一步的钻研。

    
那是我们和button1_click事件所对应的主意。注意方法的参数符合委托中的签名(既参数列表)。那我们怎么把那一个办法和事件联系起来吧,请看下边的代码。

小编们第叁定义了三个委托,然后利用event关键字,定义一个轩然大波。

14.1.5.⑤ 、多播委托的尤其处理

由此叁个委托调用多少个法子还有三个大标题。多播委托包蕴三个相继调用的嘱托集合。假诺因而信托调用的2个方法抛出了尤其,整个迭代就会告一段落。下边是MulticastIteration示例。个中定义了2个简单的委托德姆oDelegate,它没有参数,重临void。那一个委托调用方法One()和Two(),那两个艺术满意委托的参数和重返类型要求。注意方法One()抛出了一个百般:

using System;

namespace Wrox.ProCSharp.Delegates

{

public delegate void DemoDelegate();

class Program

{

static void One()

{

Console.WriteLine(“One”);

throw new Exception(“Error in one”);

}

static void Two()

{

Console.WriteLine(“Two”);

}

在Main()方法中,创造了委托d1,它引用方法One(),接着把Two()方法的地方添加到同一个信托中。调用d1信托,就足以调用那多个格局。相当在try/catch块中捕获:

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

try

{

d1();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

}

委托只调用了第多个点子。第三个主意抛出了老大,所以委托的迭代会截至,不再调用Two()方法。当调用方法的逐条没有点名时,结果会有所区别。

One

Exception Caught

注意:

多播委托包涵1个相继调用的嘱托集合。假诺通过信托调用的一个艺术抛出了那2个,整个迭代就会告一段落。即只要任一方法引发了要命,而在该方法内未捕获该特别,则该特别将传递给委托的调用方,并且不再对调用列表中后边的方式实行调用。

在那种景观下,为了制止这么些难题,应手动迭代方法列表。Delegate类定义了法子GetInvocationList(),它回到3个Delegate对象数组。以往能够利用那个委托调用与信托直接有关的艺术,捕获非常,并持续下1次迭代。

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

Delegate[] delegates = d1.GetInvocationList();

foreach (DemoDelegate d in delegates)

{

try

{

d();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

修改了代码后运转应用程序,会看出在破获了卓殊后,将持续迭代下几个方式。

One

Exception caught

Two

瞩目:其实若是在多播委托的种种具体的艺术中抓获非凡,并在个中处理,而不抛出卓殊,一样能完结多播委托的拥有办法执行达成。那种艺术与地点格局的分别在于那种格局的武汉市在函数内处的,上边那种方式的不行是在函数外面捕获并拍卖的。

         this.button1.Click += new
System.EventHandler(this.button1_Click);

完整上看,好像正是在概念三个寄托,只是在信托的概念在此以前,加了个event关键字。

14.1.⑥ 、通过委托对象来调用它所针对的函数

① 、委托实例的称号,前面包车型大巴括号中应包罗调用该信托中的方法时采用的参数。

二 、调用委托对象的Invoke()方法,Invoke后边的括号中应涵盖调用该信托中的方法时选拔的参数。

注意:实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是信托的协同调用方法。

 

留意:不管是多播委托照旧单播委托,在未曾异样处理的情状下,在三个线程的推行进度中去调用委托(委托对象所针对的函数),调用委托的执行是不会新起线程的,这几个执行只怕在原线程中的,这些对于事件也是一样的。当然,若是是在委托所针对的函数里面去运维一个新的线程那正是此外贰遍事了。

     把this.button1_Click方法绑定到this.button1.Click事变。

没错,事件的概念便是那样,因为要声惠氏(WYETH)个事件,须要七个要素:

14.2、事件

    
上面大家钻探一下C#事件处理的做事流程,首先系统会在为大家创立一个在后台监听事件的对象(假使是
button1的事件那么监听事件的正是button1),这些指标用来发出事件,借使有有些用户事件时有发生则发出相应的应用程序事件,然后实施订阅了风浪的保有办法。

一,标识提供对事件的响应的不二法门的嘱托。

14.2.① 、自定义事件

② 、简单的自定义事件(1)

二,二个类,用存款和储蓄事件的数额。即,事件要定义在类中。

14.2.1.① 、声Bellamy个寄托:

Delegate result-type delegateName ([parameters]);

这些委托能够在类A钦定义也足以在类A外定义。

     首先大家要求定义一个类来监听客户端事件,那里大家监听键盘的输入。

上边我们来为那个事件赋值。

14.2.1.贰 、声澳优个依据有个别委托的轩然大波

Event delegateName  eventName;

eventName不是二个体系,而是贰个切实可行的对象,那个实际的对象只可以在类A钦定义而无法在类A外定义。

     定义三个委托。

public void Init()
{   
    testEvent += new TestDelegate(EventSyntax_testEvent); 
    testEvent += EventSyntax_testEvent; 
}
private void EventSyntax_testEvent(string message)
{
    Console.WriteLine(message);
}
14.2.1.③ 、在类A中定义三个触发该事件的措施

ReturnType  FunctionName([parameters])

{

     ……

If(eventName != null)

{

eventName([parameters]);

或者eventName.Invoke([parameters]);

}

……

}

接触事件过后,事件所指向的函数将会被实施。那种实践是通过事件名称来调用的,就好像委托对象名相同的。

接触事件的法门只万幸A类中定义,事件的实例化,以及实例化之后的贯彻体都只可以在A类外定义。

         public delegate void UserRequest(object sender,EventArgs e);

如代码所示,大家应用了+=那些符号来为事件赋值,赋值的始末是2个委托和二个函数。

14.2.1.④ 、发轫化A类的轩然大波

在类B中定义2个类A的靶子,并且让类A对象的不得了事件指向类B中定义的章程,这一个艺术要与事件涉及的信托所界定的艺术吻合。

    
后面包车型客车object用来传递事件的产生者,后面包车型地铁伊夫ntArgs用来传递事件的细节,以后一时半刻没什么用处,一会后边的例证中将使用。

其间+=大家将她了然为【添加】。

14.2.1.五 、触发A类的风云

在B类中去调用A类中的触发事件的方法:用A类的目的去调用A类的触发事件的法子。

     上边定义1个此委托项目类型的轩然大波

代码中,大家应用二种赋值形式,但实际上都以为事件test伊芙nt添加1个委。

14.2.1.六 、程序实例

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

using System.Windows.Forms;

namespace DelegateStudy

{

    public delegate void DelegateClick (int a);

    public class butt

    {

        public event DelegateClick Click;

        public void OnClick(int a)

        {

            if(Click != null)

                Click.Invoke(a);

               //Click(a);//那种措施也是可以的

            MessageBox.Show(“Click();”);

        }

    }

    class Frm

    {

        public static void Btn_Click(int a)

        {

            for (long i = 0; i < a; i++)

                Console.WriteLine(i.ToString());

        }

        static void Main(string[] args)

        {

            butt b = new butt();

        
  //在信托中,委托对象假若是null的,直接使用+=符号,会报错,可是在事变中,开头化的时候,只可以用+=

            b.Click
+= new DelegateClick (Fm_Click); //事件是依据委托的,所以委托预计一样适用,上面包车型客车说话一样有效:b.Click
+= Fm_Click;

            //b.Click(10);错误:事件“DelegateStudy.butt.Click”只好出现在
+= 或 -= 的左手(从项目“DelegateStudy.butt”中应用时除了)

            b.OnClick (10000);                       

            MessageBox.Show(“sd234234234”);

            Console.ReadLine();

        }

   }

}

         public event UserRequest OnUserRequest;

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

14.2.二 、控件事件

遵照Windows的应用程序也是依据音讯的。那表达,应用程序是经过Windows来与用户通讯的,Windows又是使用预定义的音讯与应用程序通讯的。那个音讯是富含各样新闻的结构,应用程序和Windows使用这一个新闻决定下一步的操作。

诸如:当用户用鼠标去点击一个windows应用程序的按钮的时候,windows操作系统就会捕获到这些点击按钮的动作,那个时候它会依照捕获到的动作发送二个与之相应的预约义的信息给windows应用程序的这些按钮,windows应用程序的按钮音信处理程序会处理接收到的新闻,那些程序处理进度就是依照收到的新闻去接触相应的风浪,事件被按钮触发后,会打招呼全数的该事件的订阅者来接过这一个事件,从而执行相应的的函数。

在MFC等库或VB等支付环境推出在此以前,开发人士必须处理Windows发送给应用程序的新闻。VB和今日的.NET把那一个传送来的信息封装在事件中。假若急需响应有个别消息,就应处理相应的风浪。

     上边大家来做叁个死循环

系统提供事件

14.2.2.壹 、控件事件委托伊芙ntHandler

在控件事件中,有无数的委托,在此处介绍二个最常用的委托伊芙ntHandler,.NET
Framework中央控制件的风云很多都根据该信托,伊夫ntHandler委托已在.NET
Framework中定义了。它身处System命名空间:

Public delegate void EventHandler(object sender,EventArgs e);

         public void Run()
       {
       bool finished=false;
       do
       {
        if (Console.ReadLine()=="h")
        {
         OnUserRequest(this,new EventArgs());
        }  
       }while(!finished);
       }

C#的框架都很经典,而各样经典框架都为我们提供了有的经文事件。

14.2.2.贰 、委托伊芙ntHandler参数和重返值

事件最后会指向二个也许五个函数,函数要与事件所依照的寄托匹配。事件所指向的函数(事件处理程序)的命名规则:依据约定,事件处理程序应依据“object_event”的命名约定。object正是抓住轩然大波的靶子,而event就是被诱惑的事件。从可读性来看,应遵照这些命名约定。

第②,事件处理程序连接回到void,事件处理程序无法有再次来到值。其次是参数,只即使基于伊芙ntHandler委托的事件,事件处理程序的参数就应是object和伊夫ntArgs类型:

第③个参数接收引发风浪的目的,比如当点击有些按钮的时候,这些按钮要接触单击事件结尾实施那一个函数,那么就会把近日按钮传给sender,当有多个按钮的单击事件都指向这些函数的时候,sender的值就取决于当前被单击的充足按钮,所以能够为多少个按钮定义一个按钮单击处理程序,接着依照sender参数分明单击了哪位按钮:

if(((Button)sender).Name ==”buttonOne”)

其次个参数e是带有关于事件的任何有用音信的对象。

    
此代码不断的须要用户输入字符,假若输入的结果是h,则触发OnUserRequest事件,事件的触发者是自个儿(this),事件细节无(没有传递任何参数的伊夫ntArgs实例)。我们给这么些类取名为UserInputMonitor。

由于事件必须[标识响应措施的信托],所以这么些事件所选择的委托都有2个同步的特点,命名中带有伊夫nt。

14.2.2.三 、控件事件的其余委托

控件事件还有此外的信托,比如在窗体上有与鼠标事件波及的寄托:

Public delegate void MouseEventHandler(object sender,MouseEventArgs e);

public event MouseEventHandler MouseDown;

this.MouseDown
+= new System.Windows.Forms.MouseEventHandler(this.Form1_MouseDown);

private void Form1_MouseDown(object sender, MouseEventArgs e){};

MouseDown事件采取MouseDown伊芙ntArgs,它涵盖鼠标的指针在窗体上的的X和Y坐标,以及与事件相关的别的新闻。

控件事件中,一般第一个参数都以object
sender,第③个参数能够是轻易档次,差别的嘱托能够有两样的参数,只要它派生于伊芙ntArgs即可。

    下面我们要做的是定义客户端的类
     首先得实例化UserInputMonitor类

比如EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler等。

14.2.2.④ 、程序实例

using System;

using System.Collections.Generic;

using System.Text;

using System.Threading;

 

namespace SecondChangeEvent1

{

    // 该类用来储存关于事件的灵光新闻外,

    // 还用来储存额外的急需传给订阅者的Clock状态音信

    public class TimeInfoEventArgs : EventArgs

    {

        public TimeInfoEventArgs(int hour,int minute,int second)

        {

            this.hour = hour;

            this.minute = minute;

            this.second = second;

        }

        public readonly int hour;

        public readonly int minute;

        public readonly int second;

    }

 

    // 定义名为SecondChangeHandler的嘱托,封装不再次回到值的点子,

    // 该情势带参数,1个clock类型对象参数,三个提姆eInfo伊夫ntArgs类型对象

    public delegate void SecondChangeHandler(

       object clock,

       TimeInfoEventArgs timeInformation

    );

    // 被其它类寓指标钟(Clock)类,该类发表3个事件:SecondChange。观望该类的类订阅了该事件。

    public class Clock

    {

        // 代表小时,分钟,秒的民用变量

        int _hour;

 

        public int Hour

        {

            get { return _hour; }

            set { _hour = value; }

        }

        private int _minute;

 

        public int Minute

        {

            get { return _minute; }

            set { _minute = value; }

        }

        private int _second;

 

        public int Second

        {

            get { return _second; }

            set { _second = value; }

        }

 

        // 要发表的轩然大波

        public event SecondChangeHandler SecondChange;

 

        // 触发事件的措施

        protected void OnSecondChange(

           object clock,

           TimeInfoEventArgs timeInformation

        )

        {

            // Check if there are any Subscribers

            if (SecondChange != null)

            {

                // Call the Event

                SecondChange(clock, timeInformation);

            }

        }

 

        // 让钟(Clock)跑起来,每隔一分钟触发一遍事件

        public void Run()

        {

            for (; ; )

            {

                // 让线程Sleep一秒钟

                Thread.Sleep(1000);

 

                // 获取当前岁月

                System.DateTime dt = System.DateTime.Now;

 

                // 若是分钟变化了通告订阅者

                if (dt.Second != _second)

                {

                    // 成立TimeInfo伊夫ntArgs类型对象,传给订阅者

                    TimeInfoEventArgs timeInformation =

                       new TimeInfoEventArgs(

                       dt.Hour, dt.Minute, dt.Second);

 

                    // 公告订阅者

                    OnSecondChange(this, timeInformation);

                }

 

                // 更新景况新闻

                _second = dt.Second;

                _minute = dt.Minute;

                _hour = dt.Hour;

 

            }

        }

    }

 

 

    /* ======================= Event Subscribers
=============================== */

 

    // 多个订阅者。DisplayClock订阅了clock类的事件。它的做事是显妥善前时刻。

    public class DisplayClock

    {

        // 传入1个clock对象,订阅其SecondChangeHandler事件

        public void Subscribe(Clock theClock)

        {

            theClock.SecondChange +=

               new SecondChangeHandler(TimeHasChanged);

        }

 

        // 达成了委托匹配类型的方法

        public void TimeHasChanged(

           object theClock, TimeInfoEventArgs ti)

        {

 

            Console.WriteLine(“Current Time: {0}:{1}:{2}”,

               ti.hour.ToString(),

               ti.minute.ToString(),

               ti.second.ToString());

        }

    }

 

    // 第3个订阅者,他的办事是把方今光阴写入多个文书

    public class LogClock

    {

        public void Subscribe(Clock theClock)

        {

            theClock.SecondChange +=

               new SecondChangeHandler(WriteLogEntry);

        }

 

        // 那么些主意自然应该是把消息写入3个文书中

        // 这里大家用把新闻输出控制台代替

        public void WriteLogEntry(

           object theClock, TimeInfoEventArgs ti)

        {

            Clock a = (Clock)theClock;

            Console.WriteLine(“Logging to file: {0}:{1}:{2}”,

               a.Hour.ToString(),

               a.Minute.ToString(),

               a.Second.ToString());

        }

    }

 

    /* ======================= Test Application
=============================== */

 

    // 测试拥有程序

    public class Test

    {

        public static void Main()

        {

            // 创建clock实例

            Clock theClock = new Clock();

 

            // 创制三个DisplayClock实例,让其订阅上边创造的clock的风浪

            DisplayClock dc = new DisplayClock();

            dc.Subscribe(theClock);

 

            // 创制3个LogClock实例,让其订阅上面创造的clock的风浪

            LogClock lc = new LogClock();

            lc.Subscribe(theClock);

 

            // 让钟跑起来

            theClock.Run();

        }

    }

}

        UserInputMonitor monitor=new UserInputMonitor();

其间最经典的就是伊夫ntHandler和Routed伊芙ntHandler。

14. 3、小结

(1)、在概念事件的卓殊类A里面,能够轻易的施用事件名,能够接触;在其余类里面,事件名只好出现在
+= 或 -=
的左手来指向函数,即只可以实例化,无法直接用事件名触发。不过足以由此A类的指标来调用A类中的触发事件的函数。那是绝无仅有触发事件的办法。

(2)、不管是多播委托依旧单播委托,在未曾分外处理的意况下,在2个线程的推行进程中去调用委托(委托对象所针对的函数),调用委托的执行是不会新起线程的,那几个执行或然在原线程中的,这一个对于事件也是一致的。当然,倘使是在信托所针对的函数里面去运维3个新的线程那就是其余一遍事了。

(3)、事件是指向某一个实际的对象的,一般在该指标的所属类A中写好事件,并且写好触发事件的措施,那么这么些类A正是事件的揭橥者,然后在其他类B里面定义A的指标,并去起始化该对象的风云,让事件指向B类中的某一个切实可行的格局,B类就是A类事件的订阅者。当通过A类的目的来触发A类的风云的时候(只可以A类的靶子来触发A类的轩然大波,其余类的靶子不能够触发A类的事件,只可以订阅A类的事件,即实例化A类的风云),作为订阅者的B类会接收A类触发的轩然大波,从而使得订阅函数被实践。二个公布者能够有多少个订阅者,当发布者发送事件的时候,全体的订阅者都将收到到事件,从而执行订阅函数,不过就是是有多少个订阅者也是单线程。

原来的文章链接:
14.① 、委托 当要把办法作为实…

     然后我们定义二个格局。

EventHandler:

       private void ShowMessage(object sender,EventArgs e)
       {
           Console.WriteLine(“HaHa!!”);
       }

伊芙ntHandler定义如下

     
最后要做的是把那个艺术和事件联系起来(订阅事件),大家把它写到库户端类的构造函数里。

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

     Client(UserInputMonitor m)
      {
       m.OnUserRequest+=new
UserInputMonitor.UserRequest(this.ShowMessage);
       //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);

她带有了五个参数,即当大家为事件添加伊芙ntHandler委托后,再去触发该事件;被触发的委托将收获object
sender和伊芙ntArgs e三个参数。

       //注意这种写法是不对的,因为委托是静态的

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

      }

e:代表事件参数,即触发该事件后,事件为被触发的寄托,传递了部分参数,以有益委托在拍卖多少时,更省事。

      下边成立客户端的实例。

遵照这一个原理,我们能够分析出过多事物。

        new Client(monitor);

比如,当控件DataGrid的轩然大波被触发时,只要查看一下sender的实际类型,就足以精通,到底是DataGrid触发的风云,依旧DataGridRow或DataGridCell触发的了。

      对了,别忘了让monitor初始监听事件。

RoutedEventHandler:

         monitor.run();

Routed伊夫ntHandler即路由事件,他的定义如下

      水到渠成,代码如下:

public delegate void RoutedEventHandler(
 Object sender,
 RoutedEventArgs e
)
using System;
class UserInputMonitor
{
 public delegate void UserRequest(object sender,EventArgs e);
 //定义委托
 public event UserRequest OnUserRequest;
 //此委托类型类型的事件
 public void Run()
 {
  bool finished=false;
  do
  {
   if (Console.ReadLine()=="h")
   {
    OnUserRequest(this,new EventArgs());
   }  
  }while(!finished);
 }
}

public class Client
{
 public static void Main()
 {
  UserInputMonitor monitor=new UserInputMonitor();
  new Client(monitor);
  monitor.Run();
 }
 private void ShowMessage(object sender,EventArgs e)
 {
  Console.WriteLine("HaHa!!");
 }
 Client(UserInputMonitor m)
 {
  m.OnUserRequest+=new UserInputMonitor.UserRequest(this.ShowMessage);
  //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);
  //注意这种写法是错误的,因为委托是静态的
 }
}

Routed伊芙ntHandler也为大家提供了sender和e四个参数。

③ 、进一步商量C#中的预约义事件处理机制

但Routed伊芙ntHandler特别之处是,他的sender并不一定是动真格的的源,因为她是七个冒泡路由事件,即回涨事件。

     只怕大家发以往C#中多少事件和前面包车型客车就好像不太雷同。例如

金沙注册送58,那里假使大家有好奇心去看官方文书档案,那么会在有关的牵线中看出多个单词sender和source。

       private void textBox1_KeyPress(object
sender, System.Windows.Forms.KeyPressEventArgs e)
       {

经过那八个单词,我们会清楚的问询路由事件。简单描述一下sender和source,它们一个是发送者,二个是源。

       }

在伊芙ntHandler中,sender即source,因为它是一向事件。而在冒泡事件中,sender不一定等于source。即发送者不肯定是源。

      
this.textBox1.KeyPress+=newSystem.Windows.Forms.KeyPressEventHandler(this.textBox1_KeyPress);

上边大家用WPF来看看路由事件。

     这里运用了KeyPress伊芙ntArgs而不是伊夫ntArgs作为参数。那里运用了Key伊夫ntHandler委托,而不是伊夫ntHandler委托。

我们第二在XAML页面定义1个RadioButton按钮,然后设置他的沙盘是Button。然后分别定义各自的Click方法。

    KeyPressEventArgs是伊芙ntArgs的派生类,而Key伊夫ntHandler的评释如下

Xaml页面如下:

       public delegate void KeyEventHandler( object sender ,
KeyEventArgs e );

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

    是参数为Key伊芙ntArgs的委托。那为何KeyPress事件要如此做呢,我们得以从八个类的构造函数来找答案。

cs文件事件如下:

       public EventArgs();

 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
 }

       public KeyPressEventArgs(char keyChar);

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

     那里的keyData是怎样,是用来传递我们按下了哪个键的,哈。

梯次是先btnClild_Click后btnParent_Click。

     作者在Key伊芙ntArgs中又发现了质量

经过取得sender的连串,笔者也得以观察,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

        public char KeyChar { get; }

事件驱动编程

     进一步验证了本人的辩白。下边我们来做叁个看似的事例来救助驾驭。

事件驱动编制程序那些定义给笔者的感觉到很怪,因为一直用C#,而C#的广大框架都以事件驱动的,所以直接以为事件驱动是理所当然。

④ 、不难的自定义事件(2)

而当事件驱动设计这一个词常常出现后,反而感到奇怪。

    拿大家地方做的事例来改。

就如,每一日吃珍珠米饭,突然有一天,全数人都说籼米饭好香的感到一样,你一听就感觉到奇怪。

    
大家也定义多少个伊夫ntArgs(类似Key伊夫ntArgs)取名My伊夫ntArgs,定义三个结构函数public
My伊芙ntArgs(char keyChar),同样大家也设置相应的习性。代码如下

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

using System;
class MyMyEventArgs:EventArgs
{
 private char keyChar;
 public MyMyEventArgs(char keyChar)
 {
  this.keychar=keychar;
 }
 public char KeyChar
 {
  get
  {
   return keyChar;
  }
 }
}

故而,作者也不晓得哪些在C#里讲事件驱动编制程序。因为使用C#的框架就是应用事件驱动编制程序。

因为前几日要监听多个键了,大家得改写监听器的类中的do…while部分。改写委托,改写客户端传递的参数。好了最后代码如下,好累

事件和信托到底是什么样关联?

using System;
class MyEventArgs:EventArgs
{
 private char keyChar;
 public MyEventArgs(char keyChar)
 {
  this.keyChar=keyChar;
 }
 public char KeyChar
 {
  get
  {
   return keyChar;
  }
 }
}

class UserInputMonitor
{
 public delegate void UserRequest(object sender,MyEventArgs e);
 //定义委托
 public event UserRequest OnUserRequest;
 //此委托类型类型的事件
 public void Run()
 {
  bool finished=false;
  do
  {
   string inputString= Console.ReadLine();
   if (inputString!="") 
    OnUserRequest(this,new MyEventArgs(inputString[0]));
  }while(!finished);
 }
}

public class Client
{
 public static void Main()
 {
  UserInputMonitor monitor=new UserInputMonitor();
  new Client(monitor);
  monitor.Run();
 }
 private void ShowMessage(object sender,MyEventArgs e)
 {
  Console.WriteLine("捕捉到:{0}",e.KeyChar);
 }
 Client(UserInputMonitor m)
 {
  m.OnUserRequest+=new UserInputMonitor.UserRequest(this.ShowMessage);
  //m.OnUserRequest+=new m.UserRequest(this.ShowMessage);
  //注意这种写法是错误的,因为委托是静态的
 }
}

事件是用来多播的,并且用委托来为事件赋值,能够说,事件是依照委托来贯彻的。

但委托中也有多播,那干什么要单独弄出来二个风浪呢?

首先,存在即成立,事件一定有他存在的含义。 

事件存在的意思

自笔者对事件存在的意义是如此掌握的。大家在C#编写框架时,差不离不用委托的多播,因为委托的多播和事件存在严重的二义性。即便编写框架的人学会了动用委托的多播,但利用框架的同事或许并还不太熟习,而且C#框架中,大多是利用事件来拓展多播的。

故此委托的多播和事件联合使用的框架,会促成采取那一个框架的初级开发者很多狐疑,而那种狐疑,会产生许多不供给的题材。

比如说,
你定义了多个寄托,另二个开发者用那个委托做了个多播,当首个开发者来保证那段代码时,假如他是新手,不打听委托的多播,那就很有大概只修改了委托调用的代码。而没有去共同多播那些委托的代码。那系统就产生了隐形的bug。

那么,事件和信托到底是如何关联啊?

事件与寄托的确存在复杂的关系,怎么讲都以天经地义的。但,C#开发者只必要牢记,他们俩不妨即可。在C#事件是事件,委托是信托。两者就就好像int和string一样,没有其他关系。

原因相当粗略,学习的进度中尽量下跌概念混淆。而且,在C#支出中,好的架构者也无独有偶会将事件和信托分离,所以,就觉得事件和寄托没有关联即可。

结语

骨子里事件很好理解,一点不复杂。笔者在写那篇小说的进程中,也没悟出什么尤其的或然说比较高档的用法。

但忠实的运用场景中,笔者的感觉是,随着MVVM的成材,事件实际在被逐步遗弃。固然微软做了不胜枚举经典的事件驱动框架。但那都以过去了。

诸如WPF纵然支持事件驱动,但MVVM在WPF下的显示堪称完美,所以WPF下的轩然大波大约从不人用了。

再比如前端的Angularjs等框架,提供了上品的MVVM使用效果,也让新的前端设计师慢慢吐弃了轩然大波。

故此,事件在以往的编制程序中,很或然将不在有那么首要的地位了。但学好事件,对于大家知晓微软框架,依旧有十分大帮扶的。

C#语法——元组类型

C#语法——泛型的两种采取

C#语法——await与async的不利打开药格局

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

我对C#的认知。


注:此小说为原创,欢迎转发,请在篇章页面明显地点给出此文链接!
若你认为那篇文章还行,请点击下右下角的【推荐】,格外感激!
若果你认为那篇作品对您有所协助,那就无妨支付宝小小打赏一下吧。 

金沙注册送58 4

 

相关文章

网站地图xml地图