委托和事件,深刻浅出WPF

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

  固然对事件一点都不打听依然是暧昧的话,建议先去看张子阳的信托与事件的篇章(相比较长,恐怕看完了,也记不清看这一篇了,没事,小编会原谅你的),废话不多说,起始进入正题。本记录不是记录守旧的事件(CL凯雷德事件),而是记录WPF中常用的轩然大波——路由事件,由于路由事件“传播”的光阴是本着可视树传播的,所以在笔录发轫在此以前照旧询问一下逻辑树和可视树。

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

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

事件最普遍的比喻就是订阅,即,固然您订阅了自身的博客,那么,当自身宣布新博客的时候,你就会获取关照。

 一 、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree)和可视树(Visual
Tree),XAML是抒发WPF的一棵树。逻辑树完全是由布局组件和控件构成。假使大家把逻辑树延伸至Template组件级别,大家就赢得了可视树,所以可视树把树分的更细致。由于本记录重在笔录事件,所以不做过多宣布逻辑树和可视树的始末。关于逻辑树和可视树的界别可以参见。

初稿链接:

    
在写代码前我们先来熟谙.net框架花潮事件有关的类和信托,通晓C#中预定义事件的拍卖。

而这一个进度正是事件,可能说是事件运维的轨道。

贰 、路由事件

14.1、委托

当要把艺术作为实参传送给其余方法的形参时,形参必要运用委托。委托是三个门类,是一个函数指针类型,这么些项目将该信托的实例化对象所能指向的函数的细节封装起来了,即分明了所能指向的函数的签字,也正是限制了所能指向的函数的参数和重返值。当实例化委托的时候,委托对象会指向某多少个万分的函数,实质就是将函数的地点赋值给了该信托的对象,然后就能够经过该信托对象来调用所针对的函数了。利用委托,程序员能够在信托对象中封装一个艺术的引用,然后委托对象作为形参将被传给调用了被引用方法的代码,而不供给精晓在编写翻译时刻具体是哪些方法被调用。

相似的调用函数,大家都不会去行使委托,因为一旦只是单纯的调用函数,使用委托更麻烦一些;然而若是想将函数作为实参,传递给某些函数的形参,那么形参就自然要动用委托来接超过实际参,一般选择模式是:在函数外面定义委托对象,并对准有个别函数,再将以此指标赋值给函数的形参,形参也是该委托类型的靶子变量,函数里面再经过形参来调用所针对的函数。

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

事件是分散,以本身的博客为着力,向具有订阅者发送音信。大家把那种分散称之为[多播]。

2.壹 、小记事件

  假使看完了寄托与事件的稿子,相信会对事件有更进一步的认识了,但要么要把一部分基础的地点再记录一下。贰个轩然大波要有下边多少个成分,才会变的有意义:

    • 事件的拥有者(sender)——即新闻的发送者。
    • 事件发送的音信(伊夫ntAgs)
    • 事件的响应者——音讯的收信人(对象)。
    • 响应者的处理器——音信的接受者要对音讯作出处理的点子(方法名)。
    • 响应者对发送者事件的订阅

14.1.① 、定义委托

语法如下

delegate  result-type   Identifier ([parameters]);

说明:

result-type:重返值的体系,和办法的回来值类型一致

Identifier:委托的称号

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

小结

当定义了委托随后,该信托的对象自然能够而且也只能指向该信托所界定的函数。即参数的个数、类型、顺序都要同盟,重临值的门类也要同盟。

因为定义委托也正是是定义1个新类,所以能够在定义类的另各地方定义委托,既可以在3个类的中间定义,那么此时就要通过此类的类名来调用这一个委托(委托必须是public、internal),也足以在别的类的表面定义,那么此时在命名空间中与类的级别是如出一辙的。依据定义的可知性,能够在信托定义上添加一般的拜访修饰符:当委托定义在类的外界,那么能够加上public、internal修饰符;如果委托定义到类的里边,那么能够添加public、 private、 protected、internal。一般委托都是概念在类的外面包车型大巴。

     EventHandler是一个委托申明如下

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

2.2 初试路由事件

  大家建立三个winform项目,然后在窗体上添加一个按钮,双击添加三个处理器,会意识private
void btn_Click(object sender, 伊夫ntArgs
e)处理器要拍卖音讯是伊芙ntArgs类型的,那里对应的是观念的事件(大家叫它CL翼虎事件)。同样大家树立贰个WPF项目,然后添加3个按钮,双击添加二个总结机,会发觉private
void Button_Click(object sender, Routed伊夫ntArgs
e)处理器要拍卖的新闻是Routed伊芙ntArgs类型的,那里对应的是路由事件。两者有哪些分别吧。让大家先看看CL奥迪Q5事件的害处,如同(this.btn.Click
+= new
System.伊芙ntHandler(this.btn_Click);)每1个音讯都是从发送到响应的一个进程,当二个处理器要用数十次,必须树立显式的点对点订阅关系(窗体对按钮事件的订阅,若是是再有二个按钮的话,就要再来三遍订阅);还有1个弊病是:事件的宿主必须能够一直访问事件的响应者,不然不可能建立订阅关系(如有四个零部件,点击组件一的按钮,想让组件二响应事件,那么就让组件二向组件一的按钮揭露3个得以访问的轩然大波,那样只要再多多少个嵌套,会现出事件链,有揭发如若暴光不当就存在着威迫)。路由事件除了能很好的消除地方的难题,还有三个是路由事件在有路的景观下,能很好的遵照鲜明的措施传播事件,因为XAML的树状结构,构成了一条条的征程,所以在WPF中,引入了路由事件。举个例子:假若窗体要以相同的方式处理五个按钮的风浪,大家就足以用一句代码就解决了,this.AddHandler(Button.Click伊夫nt,
new
Routed伊夫ntHandler(this.ButtonClicked));这样就减少代码量。上面通过1个例子初试一下路由事件。 给出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));订阅事件时,第2个参数是路由事件类型,在此处用的是Button的Click伊芙nt,就好像倚重属性一样,类名加上重视属性,那里是类名加上路由事件。其它贰个是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:

 

美高梅开户网址 1

图1

默认的路由音讯里面属性有七个,如图2,可以活动转到定义看一下其品质代表的含义。

美高梅开户网址 2

图2

 

14.1.二 、实例化委托

Identifier  objectName  =  new  Identifier( functionName);

实例化委托的真相正是将某些函数的地址赋值给委托对象。在那里:

Identifier :这么些是信托名字。

objectName :委托的实例化对象。

functionName:是该信托对象所针对的函数的名字。对于这几个函数名要特别注意:定义这么些委托对象自然是在类中定义的,那么只要所指向的函数也在此类中,不管该函数是静态还是非静态的,那么就径直写函数名字就能够了;假诺函数是在别的类里面定义的public、

internal,然则只假如静态,那么就径直用类名.函数名,假如是非静态的,那么就类的靶子名.函数名,那些函数名与该对象是有涉嫌的,比如假设函数中冒出了this,表示的正是对脚下指标的调用。

          public delegate void EventHandler( object 委托和事件,深刻浅出WPF。sender , EventArgs
e )

当在窗体中式点心击按钮,移动鼠标等事件时,相应的后台程序会吸收接纳公告,再实践代码。

 2.3自定义路由事件

  通过地点的小试路由事件,应该适中由事件有些理解了,下边就记录一下怎么着自定义贰个路由事件。差不多分成多少个步骤:1.声称并注册路由事件,2.为路由事件添加CLLAND事件包装,3.创设能够激励路由事件的章程。纪念一下凭借属性,前七个步骤应该和路由事件很一般吧。上边将八个步骤分开来证实:

率先步:表明并注册路由事件       

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

其次步:为路由事件添加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);
        }

 上面大家就来兑现3个简约的自定义路由作用,当路由飘过3个控件的时日,展现通过该控件的日子。 下边介绍的大都了,所以就向来上代码,有亟待表明的话,再三个个解释。

下面是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:

美高梅开户网址 3

图3

  注意上面包车型客车一条龙代码,在宣称和定义路由事件时,第四个参数,委托的品类和处理器方法的的参数一定要平等,不然会报错,能够把下部一句中的ReportTimeRoute伊夫ntHandler换来Routed伊芙ntHandler试试,会油不过生:不恐怕从文本“ReportTimeHandler”制造“ReportTime”。”,行号为“5”,行任务为“30”,的标题,那里关键的原故便是信托的参数和Routed伊芙ntHandler的参数分裂,就算都是(sender, 
e);不过e的连串已经爆发了扭转,成为了Report提姆e伊夫ntArgs类型的。所以在动用在此之前,声宾博个委托就足以了。还有个点子是应用伊芙ntHandler<ReportTime伊夫ntArgs>替换ReportTimeRoute伊夫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前边加括号和实参,然后把它传送给委托变量。调用方法一般会回去三个不能够给予委托变量的常备对象,除非那一个主意重临的是3个匹配的嘱托对象。不问可见:只可以把相匹配的章程的地址赋予委托变量。

寄托估算能够在急需委托实例化的别样地点接纳,就跟定义普通的委托对象是一律的。委托揣度也足以用来事件,因为事件基于委托(参见本章后边的内容)。

    
注意那里的参数,前者是3个指标(其实那里传递的是指标的引用,假若是button1的click事件则sender就是button1),前面是带有事件数量的类的基类。

事件的概念

 三、总结

  本篇记录的剧情就算不多,然而感觉记录的时辰专程讨厌,首即使因为对事件的多少个组成都部队分还不是十分熟悉,而且在驾驭路由事件时,还要先清楚逻辑树与可视树。最终照旧把这一章看完了,但以此只是始于。

  文章首要记录了路由事件的在可视树上的传遍以及自定义路由事件的贯彻。假设在小说有例外的眼光或建议,欢迎沟通! 下一篇:《长远浅出WPF》笔记——命令篇

 

14.1.④ 、匿名方式

到最近截至,要想使委托工作,方法必须已经存在。但实例化委托还有其余一种办法:即透过匿名方式。

用匿超情势定义委托的语法与如今的概念并没有区分。但在实例化委托时,就有分别了。上面是一个分外简单的控制台应用程序,表明了哪些使用匿超级模特式:

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中定义,它带一个字符串参数。有分别的是Main方法。在定义anonDel时,不是传递已知的措施名,而是使用二个简约的代码块:它日前是生死攸关字delegate,前面是多个参数:

delegate(string param)

{

  param += mid;

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

  return param;

};

匿超形式的帮助和益处是缩小了要编写的代码。方法仅在有嘱托行使时才定义。在为事件定义委托时,那是特别理解的。(本章前边探究事件。)那促进下落代码的复杂,尤其是概念了一点个事件时,代码会显得比较简单。使用匿名形式时,代码执行得不太快。编写翻译器仍定义了一个措施,该措施只有多少个活动钦命的称呼,大家不必要明白这些称谓。

在选拔匿有名的模特式时,必须依据多少个规则:

一 、在匿名情势中不能够利用跳转语句跳到该匿名情势的外部,反之亦然:匿名格局外部的跳转语句不能够跳到该匿名格局的里边。

贰 、在匿名情势内部无法访问不安全的代码。另外,也不可能访问在匿超级模特式外部使用的ref和out参数。但足以行使在匿名格局外部定义的其余变量。方法内部的变量、方法的参数能够肆意的采用。

即使急需用匿名情势多次编辑同2个功效,就不要使用匿超方式。而编写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 的章程列表中。

     那里定义了2个伊芙ntHandler类型的事件Click

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

14.1.5.② 、委托运算符 +=

objectName  +=  new  Identifier( functionName1);

或者

objectName  +=  functionName1;

此地的“+=”号表示在原本的法子列表不变的动静下,将 functionName1  加入到     objectName 的办法列表中。能够在点子列表中加上多少个一样的点子,执行的时候也会实施完全部的函数,哪怕有相同的,就会一再推行同3个办法。

留神: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.⑤ 、多播委托的老大处理

透过1个信托调用多少个主意还有三个大难题。多播委托包罗三个挨家挨户调用的委托集合。假设经过信托调用的一个办法抛出了更加,整个迭代就会停下。下边是MulticastIteration示例。个中定义了一个简练的委托德姆oDelegate,它没有参数,再次来到void。这么些委托调用方法One()和Two(),那多个点子餍足委托的参数和再次回到类型须求。注意方法One()抛出了3个特别:

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()方法的地点添加到同1个信托中。调用d1信托,就能够调用那三个章程。非常在try/catch块中抓获:

static void Main()

{

DemoDelegate d1 = One;

d1 += Two;

try

{

d1();

}

catch (Exception)

{

Console.WriteLine(“Exception caught”);

}

}

}

}

委托只调用了第三个法子。第三个法子抛出了要命,所以委托的迭代会甘休,不再调用Two()方法。当调用方法的依次没有点名时,结果会有所差别。

One

Exception Caught

注意:

多播委托包蕴四个依次调用的寄托集合。要是经过信托调用的贰个主意抛出了万分,整个迭代就会终止。即只要任一方法引发了尤其,而在该措施内未捕获该特别,则该越发将传递给委托的调用方,并且不再对调用列表中前面包车型客车主意开始展览调用。

在那种情形下,为了幸免这一个标题,应手动迭代方法列表。Delegate类定义了章程GetInvocationList(),它回到二个Delegate对象数组。今后得以行使这几个委托调用与信托直接相关的办法,捕获格外,并持续下三回迭代。

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);

一体化上看,好像正是在概念2个寄托,只是在信托的概念此前,加了个event关键字。

14.1.六 、通过委托对象来调用它所指向的函数

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

二 、调用委托对象的Invoke()方法,Invoke前面包车型客车括号中应包罗调用该信托中的方法时行使的参数。

只顾:实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是寄托的同台调用方法。

 

留意:不管是多播委托依旧单播委托,在尚未异样处理的情事下,在三个线程的施行进程中去调用委托(委托对象所指向的函数),调用委托的实践是不会新起线程的,这么些执行大概在原线程中的,那几个对于事件也是一模一样的。当然,如若是在信托所指向的函数里面去运行三个新的线程那便是其它三回事了。

     把this.button1_Click方法绑定到this.button1.Click轩然大波。

正确,事件的定义便是那般,因为要声明一(Wissu)(Beingmate)个轩然大波,必要多少个成分:

14.2、事件

    
下边大家探讨一下C#事件处理的办事流程,首先系统会在为大家创立3个在后台监听事件的目的(尽管是
button1的轩然大波那么监听事件的便是button1),这一个目的用来发闯事件,假设有有些用户事件发生则发出相应的应用程序事件,然后实施订阅了轩然大波
的有着办法。

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

14.2.① 、自定义事件

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

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

14.2.1.① 、声美赞臣个委托:

Delegate result-type delegateName ([parameters]);

其一委托能够在类A钦命义也足以在类A外定义。

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

下边我们来为这些事件赋值。

14.2.1.② 、声爱他美(Aptamil)(Beingmate)个依据有个别委托的事件

Event delegateName  eventName;

eventName不是三个档次,而是二个有血有肉的目的,那么些现实的靶子只可以在类A钦定义而无法在类A外定义。

     定义3个寄托。

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

ReturnType  FunctionName([parameters])

{

     ……

If(eventName != null)

{

eventName([parameters]);

或者eventName.Invoke([parameters]);

}

……

}

接触事件之后,事件所针对的函数将会被执行。那种实践是由此事件名称来调用的,就像是委托对象名相同的。

接触事件的格局只万幸A类中定义,事件的实例化,以及实例化之后的兑现体都只可以在A类外定义。

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

如代码所示,我们应用了+=这么些标记来为事件赋值,赋值的始末是八个寄托和三个函数。

14.2.1.肆 、发轫化A类的风浪

在类B中定义2个类A的靶子,并且让类A对象的老大事件指向类B中定义的办法,这几个措施要与事件涉及的信托所界定的点子吻合。

    
前边的object用来传递事件的产生者,前边的伊夫ntArgs用来传递事件的细节,将来如今没什么用处,一会后头的例证中校使用。

其中+=大家将他驾驭为【添加】。

14.2.1.伍 、触发A类的轩然大波

在B类中去调用A类中的触发事件的不二法门:用A类的对象去调用A类的触及事件的情势。

     上面定义一个此委托项目类型的风浪

代码中,大家运用三种赋值格局,但骨子里都是为事件test伊夫nt添加3个委。

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;

其次种将函数间接【添加】到事件中,编写翻译时也会把函数转换来委托【添加】到事件中。

14.2.② 、控件事件

依照Windows的应用程序也是基于新闻的。这表明,应用程序是经过Windows来与用户通讯的,Windows又是使用预订义的音讯与应用程序通讯的。那几个音讯是富含各类音信的结构,应用程序和Windows使用那一个音信决定下一步的操作。

比如说:当用户用鼠标去点击1个windows应用程序的按钮的时候,windows操作系统就会捕获到这几个点击按钮的动作,那一个时候它会依照捕获到的动作发送1个与之相应的预约义的消息给windows应用程序的那几个按钮,windows应用程序的按钮音信处理程序会处理接收到的新闻,那些程序处理进程正是依照收到的音讯去接触相应的轩然大波,事件被按钮触发后,会通告全数的该事件的订阅者来收取那几个事件,从而执行相应的的函数。

在MFC等库或VB等成本条件推出以前,开发人员必须处理Windows发送给应用程序的音讯。VB和明天的.NET把这几个传送来的新闻封装在事件中。假若供给响应有些音信,就应处理相应的轩然大波。

     上面大家来做2个死循环

系统提供事件

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

在控件事件中,有很多的委托,在那里介绍3个最常用的委托伊夫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。

是因为事件必须[标识响应措施的嘱托],所以那一个事件所运用的委托都有二个联机的特色,命名中隐含伊芙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的委托,封装不重返值的法子,

    // 该方式带参数,一个clock类型对象参数,1个TimeInfo伊芙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)跑起来,每隔一分钟触发1回事件

        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

    {

        // 传入贰个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());

        }

    }

 

    // 第一个订阅者,他的劳作是把当前时光写入1个文本

    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);

 

            // 创造一个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)、不管是多播委托依旧单播委托,在一向不新鲜处理的景观下,在3个线程的实践进度中去调用委托(委托对象所针对的函数),调用委托的推行是不会新起线程的,这几个执行大概在原线程中的,那些对于事件也是均等的。当然,借使是在信托所针对的函数里面去运营一个新的线程那正是别的一遍事了。

(3)、事件是指向某3个现实的指标的,一般在该对象的所属类A中写好事件,并且写好触发事件的法子,那么这几个类A就是事件的公布者,然后在其他类B里面定义A的靶子,并去早先化该对象的事件,让事件指向B类中的某2个现实的法门,B类正是A类事件的订阅者。当通过A类的对象来触发A类的事件的时候(只好A类的指标来触发A类的风浪,别的类的靶子不可能触发A类的轩然大波,只可以订阅A类的事件,即实例化A类的事件),作为订阅者的B类会接收A类触发的风云,从而使得订阅函数被实践。2个发布者能够有五个订阅者,当公布者发送事件的时候,全数的订阅者都将接收到事件,从而执行订阅函数,然而正是是有三个订阅者也是单线程。

原版的书文链接:
14.一 、委托 当要把措施作为实…

     然后大家定义3个措施。

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#中稍微事件和前边的就像不太一致。例如

此地固然大家有好奇心去看官方文书档案,那么会在连锁的介绍中观望五个单词sender和source。

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

通过那四个单词,大家会清楚的问询路由事件。不难描述一下sender和source,它们3个是发送者,1个是源。

       }

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

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

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

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

小编们第二在XAML页面定义二个RadioButton按钮,然后设置他的模版是Button。然后分别定义各自的Click方法。

    KeyPress伊夫ntArgs是伊芙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。

     小编在KeyEventArgs中又发现了品质

经过获取sender的品类,笔者也能够看看,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

        public char KeyChar { get; }

事件驱动编制程序

     进一步表达了本身的反驳。上边大家来做1个好像的事例来辅助精通。

事件驱动编制程序那么些概念给笔者的感觉到很怪,因为直接用C#,而C#的成都百货上千框架都以事件驱动的,所以平素认为事件驱动是自然。

肆 、简单的自定义事件(2)

而当事件驱动设计那么些词平时出现后,反而觉得蹊跷。

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

就类似,每日吃黑米饭,突然有一天,全数人都说珍珠米饭好香的感觉一样,你一听就感觉到蹊跷。

    
我们也定义3个伊夫ntArgs(类似Key伊夫ntArgs)取名MyEventArgs,定义三个布局函数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#的认知。


注:此小说为原创,欢迎转发,请在篇章页面分明地点给出此文链接!
若你认为那篇文章还不易,请点击下右下角的【推荐】,万分多谢!
如若您认为那篇文章对你有所扶助,那就不要紧支付宝小小打赏一下啊。 

美高梅开户网址 4

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图