威尼斯人线上娱乐

委托和事件,深远浅出WPF

30 3月 , 2019  

事件是C#的根基之一,学好事件对于掌握.NET框架大有利益。

  要是对事件一点都不通晓如故是笼统的话,提出先去看张子阳的委托与事件的篇章(比较长,可能看完了,也记不清看这一篇了,没事,小编会原谅你的),废话不多说,开端进入正题。本记录不是记录古板的风浪(CLMurano事件),而是记录WPF中常用的轩然大波——路由事件,由于路由事件“传播”的日子是本着可视树传播的,所以在笔录起初以前依旧摸底一下逻辑树和可视树。

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

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

事件最常见的比方就是订阅,即,假如你订阅了自家的博客,那么,当自家发布新博客的时候,你就会收获布告。

 一 、逻辑树和可视树

  在WPF中有二种树:逻辑树(Logical Tree)和可视树(Visual
Tree),XAML是发布WPF的一棵树。逻辑树完全是由布局组件和控件构成。假若我们把逻辑树延伸至Template组件级别,大家就赢得了可视树,所以可视树把树分的更密切。由于本记录重在笔录事件,所以不做过多发表逻辑树和可视树的情节。关于逻辑树和可视树的不相同能够参考。

初稿链接:

    
在写代码前我们先来熟习.net框架二月事件有关的类和嘱托,通晓C#中预订义事件的处理。

而那一个进程就是事件,只怕说是事件运转的轨迹。

贰 、路由事件

14.1、委托

当要把办法作为实参传送给其它艺术的形参时,形参供给利用委托。委托是一个种类,是2个函数指针类型,那一个系列将该信托的实例化对象所能指向的函数的细节封装起来了,即鲜明了所能指向的函数的签署,也正是限制了所能指向的函数的参数和重回值。当实例化委托的时候,委托对象会针对某一个男才女貌的函数,实质正是将函数的地址赋值给了该信托的目的,然后就足以经过该信托对象来调用所针对的函数了。利用委托,程序员能够在信托对象中封装1个主意的引用,然后委托对象作为形参将被传给调用了被引述方法的代码,而不需求明白在编写翻译时刻具体是哪些方法被调用。

相似的调用函数,大家都不会去行使委托,因为倘诺只是单独的调用函数,使用委托更麻烦一些;可是假如想将函数作为实参,传递给有些函数的形参,那么形参就必将要利用委托来收取实参,一般选用格局是:在函数外面定义委托对象,并针对有个别函数,再将这几个指标赋值给函数的形参,形参也是该委托类型的靶子变量,函数里面再通过形参来调用所针对的函数。

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

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

2.① 、小记事件

  即使看完了委托与事件的篇章,相信会对事件有更进一步的认识了,但依旧要把一部分基础的地点再记录一下。一个事件要有下边多少个要素,才会变的有含义:

    • 事件的拥有者(sender)——即音信的发送者。
    • 事件发送的音信(伊芙ntAgs)
    • 事件的响应者——音讯的接收者(对象)。
    • 响应者的微处理器——消息的接受者要对音信作出处理的措施(方法名)。
    • 响应者对发送者事件的订阅

14.1.① 、定义委托

语法如下

delegate  result-type   Identifier ([parameters]);

说明:

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

Identifier:委托的称号

parameters:参数,要引用的艺术带的参数

小结

当定义了寄托随后,该信托的对象自然能够同时也只可以指向该信托所界定的函数。即参数的个数、类型、顺序都要同盟,重返值的品种也要合营。

因为定义委托也等于是定义2个新类,所以能够在定义类的别样地点定义委托,既能够在二个类的里边定义,那么此时将要通过此类的类名来调用这些委托(委托必须是public、internal),也得以在其余类的外表定义,那么此时在命名空间中与类的级别是一样的。根据定义的可知性,能够在委托定义上添加一般的访问修饰符:当委托定义在类的外面,那么能够增加public、internal修饰符;假设委托定义到类的内部,那么能够加上public、 private、 protected、internal。一般委托都是概念在类的外场的。

     EventHandler是三个委托表明如下

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

2.2 初试路由事件

  大家树立多少个winform项目,然后在窗体上添加贰个按钮,双击添加三个计算机,会发觉private
void btn_Click(object sender, 伊芙ntArgs
e)处理器要拍卖音讯是伊芙ntArgs类型的,这里对应的是价值观的轩然大波(大家叫它CL牧马人事件)。同样大家建立3个WPF项目,然后添加三个按钮,双击添加四个电脑,会意识private
void Button_Click(object sender, Routed伊芙ntArgs
e)处理器要处理的音信是Routed伊芙ntArgs类型的,那里对应的是路由事件。两者有如何分别吧。让大家先看看CLR事件的弊病,就像(this.btn.Click
+= new
System.伊夫ntHandler(this.btn_Click);)每贰个音信都以从发送到响应的2个经过,当3个处理器要用数十次,必须树立显式的点对点订阅关系(窗体对按钮事件的订阅,借使是再有2个按钮的话,就要再来一回订阅);还有二个害处是:事件的宿主必须能够一向访问事件的响应者,不然不也许建立订阅关系(如有多少个零部件,点击组件一的按钮,想让组件二响应事件,那么就让组件二向组件一的按钮揭发三个方可访问的轩然大波,这样假使再多多少个嵌套,会冒出事件链,有揭示借使揭破不当就存在着威逼)。路由事件除了能很好的缓解地方的题材,还有二个是路由事件在有路的状态下,能很好的遵照分明的法门传播事件,因为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,就像是注重属性一样,类名加上信赖属性,这里是类名加上路由事件。别的一个是e.OriginalSource与e.Source的界别。由于音讯每传一站,都要把新闻交个3个控件(此控件成为了音信的发送地方),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 sender , EventArgs
e )

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

 2.3自定义路由事件

  通过地点的小规模试制路由事件,应该适量由事件有个别精晓了,下边就记录一下什么样自定义3个路由事件。差不多分为四个步骤:1.扬言并注册路由事件,2.为路由事件添加CLPAJERO事件包装,3.创制能够激发路由事件的艺术。回想一下依靠属性,前四个步骤应该和路由事件很相像吧。下边将多个步骤分开来表明:

先是步:阐明并注册路由事件       

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

其次步:为路由事件添加CLTucson事件包装

       /*包装事件
        *这里与传统的数据差别是把+=和-=换成了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);
        }

 上面我们就来落到实处1个简单易行的自定义路由功效,当路由飘过贰个控件的光阴,突显通过该控件的时光。 上边介绍的基本上了,所以就径直上代码,有须求解释的话,再二个个表明。

下面是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试试,会产出:不可能从文本“Report提姆eHandler”创立“ReportTime”。”,行号为“5”,行任务为“30”,的题材,那里重要的原由正是信托的参数和Routed伊夫ntHandler的参数分裂,纵然都以(sender, 
e);可是e的类型已经发生了变更,成为了ReportTime伊芙ntArgs类型的。所以在运用以前,注解一(Wissu)个寄托就能够了。还有个措施是使用伊芙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后边加括号和实参,然后把它传送给委托变量。调用方法一般会回来三个不可能加之委托变量的一般性对象,除非那个办法再次回到的是二个13分的寄托对象。由此可知:只好把相匹配的艺术的地方赋予委托变量。

寄托臆度能够在急需委托实例化的别的地点使用,就跟定义普通的寄托对象是平等的。委托预计也足以用于事件,因为事件基于委托(参见本章后边的内容)。

    
注意那里的参数,前者是一个对象(其实那里传递的是目的的引用,借使是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,前边是3个参数:

delegate(string param)

{

  param += mid;

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

  return param;

};

匿名方式的帮助和益处是减掉了要编写制定的代码。方法仅在有嘱托行使时才定义。在为事件定义委托时,那是越发显然的。(本章前面商讨事件。)那有助于下降代码的扑朔迷离,越发是概念了某个个事件时,代码会显得相比简单。使用匿名情势时,代码执行得不太快。编写翻译器仍定义了贰个方法,该办法只有三个机动钦赐的名目,大家不须要了然这些称呼。

在选拔匿有名的模特式时,必须听从三个规则:

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

贰 、在匿名情势内部不能够访问不安全的代码。别的,也不可能访问在匿有名的模特式外部使用的ref和out参数。但足以动用在匿名形式外部定义的其余变量。方法内部的变量、方法的参数能够私自的使用。

万一急需用匿名格局数次编写制定同2个职能,就无须使用匿名形式。而编写贰个内定的点子相比好,因为该方法只需编写二次,未来可经过名称引用它。

    
下边我们钻探一下Button类看看当中的轩然大波表明(使用WinCV工具查看),以Click事件为例。

法定对事件的印证是这样的:类或对象能够通过事件向此外类或对象文告发出的相干业务。

14.1.五 、多播委托

前边使用的各类委托都只含有三个格局调用,调用委托的次数与调用方法的次数相同,倘若要调用五个法子,就须要反复给委托赋值,然后调用这些委托。

委托也能够包含两个艺术,那时候要向委托对象中添加多少个格局,那种委托称为多播委托,多播委托有1个措施列表,若是调用多播委托,就可以连续调用八个格局,即先实施某2个格局,等该方法执行到位未来再举办此外三个办法,那一个方法的参数都以同一的,这个办法的推行是在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 的措施列表中。能够在章程列表中加上四个一律的艺术,执行的时候也会实施完全部的函数,哪怕有一样的,就会频繁实施同2个形式。

留意:objectName 必须是早就赋值了的,不然在概念的时候一贯行使该符号:

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

Identifier  objectName  +=  functionName1;就会报错。

    
前边的内容都以C#在类库中早就为大家定义好了的。下边大家来看编程时产生的代码。

上边大家来看最基础的轩然大波定义。

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

objectName  -=  new  Identifier( functionName1);

或者

objectName  -=  functionName1;

此处的“-=”号表示在 objectName 的办法列表中减去3个functionName1。可以在点子列表中频仍减去划一的主意,减三次只会减1个主意,假如列表中无此办法,那么减就从未意思,对原始列表无影响,也不会报错。

只顾: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事件所对应的办法。注意方法的参数符合委托中的签名(既参数列表)。那大家怎么把那么些法子和事件联系起来吧,请看上边包车型地铁代码。

大家率先定义了1个信托,然后利用event关键字,定义贰个轩然大波。

14.1.5.伍 、多播委托的不行处理

因此贰个信托调用两个情势还有三个大难点。多播委托包蕴1个依次调用的嘱托集合。若是经过委托调用的1个艺术抛出了拾分,整个迭代就会告一段落。下边是MulticastIteration示例。个中定义了三个简便的委托德姆oDelegate,它从不参数,再次回到void。那几个委托调用方法One()和Two(),那五个章程知足委托的参数和重返类型必要。注意方法One()抛出了一个不行:

using System;

namespace Wrox.ProCSharp.Delegates

{

public delegate void DemoDelegate();

委托和事件,深远浅出WPF。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

注意:

多播委托包罗3个挨家挨户调用的信托集合。尽管经过信托调用的四个主意抛出了丰硕,整个迭代就会甘休。即只要任一方法引发了老大,而在该措施内未捕获该尤其,则该越发将传递给委托的调用方,并且不再对调用列表中前面包车型客车法门实行调用。

在那种气象下,为了制止那几个标题,应手动迭代方法列表。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”);

}

}

}

修改了代码后运转应用程序,会看到在抓获了丰裕后,将一连迭代下3个措施。

One

Exception caught

Two

在意:其实只要在多播委托的每一种具体的方法中捕获十分,并在里头处理,而不抛出十分,一样能落到实处多播委托的具备办法执行完成。那种办法与地点情势的界别在于那种措施的孝感市在函数内处的,上边那种方式的充足是在函数外面捕获并拍卖的。

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

一体化上看,好像就是在概念四个信托,只是在委托的定义在此以前,加了个event关键字。

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

一 、委托实例的名称,后边的括号中应涵盖调用该信托中的方法时选拔的参数。

贰 、调用委托对象的Invoke()方法,Invoke后边的括号中应包涵调用该信托中的方法时使用的参数。

小心:实际上,给委托实例提供括号与调用委托类的Invoke()方法完全相同。因为Invoke()方法是委托的1头调用方法。

 

专注:不管是多播委托照旧单播委托,在未曾例外处理的情事下,在多少个线程的施行进程中去调用委托(委托对象所指向的函数),调用委托的实践是不会新起线程的,那几个执行或许在原线程中的,那么些对于事件也是一律的。当然,假设是在信托所针对的函数里面去运转1个新的线程那正是此外1遍事了。

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

毋庸置疑,事件的概念正是那样,因为要声澳优个事件,须求五个成分:

14.2、事件

    
上边大家研究一下C#事件处理的做事流程,首先系统会在为大家创制3个在后台监听事件的目的(如若是
button1的风浪那么监听事件的正是button1),那几个目的用来发生事件,若是有有个别用户事件发生则发出相应的应用程序事件,然后实施订阅了轩然大波
的有着办法。

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

14.2.① 、自定义事件

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

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

14.2.1.① 、声美素佳儿个信托:

Delegate result-type delegateName ([parameters]);

本条委托能够在类A钦赐义也得以在类A外定义。

     首先咱们供给定义2个类来监听客户端事件,那里我们监听键盘的输入。

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

14.2.1.② 、声圣元(Dumex)个依照某些委托的轩然大波

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中定义1个触发该事件的艺术

ReturnType  FunctionName([parameters])

{

     ……

If(eventName != null)

{

eventName([parameters]);

或者eventName.Invoke([parameters]);

}

……

}

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

接触事件的章程只幸好A类中定义,事件的实例化,以及实例化之后的完毕体都只幸而A类外定义。

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

如代码所示,我们运用了+=那几个标记来为事件赋值,赋值的始末是贰个委托和3个函数。

14.2.1.四 、开始化A类的轩然大波

在类B中定义1个类A的对象,并且让类A对象的特别事件指向类B中定义的不二法门,这些法子要与事件涉及的信托所界定的方法吻合。

    
前边的object用来传递事件的爆发者,前边的伊芙ntArgs用来传递事件的底细,未来权且没什么用处,一会背后的例证大校使用。

其间+=我们将她明白为【添加】。

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

在B类中去调用A类中的触发事件的措施:用A类的对象去调用A类的触及事件的艺术。

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

代码中,大家运用三种赋值形式,但实质上都以为事件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;

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

14.2.② 、控件事件

依照Windows的应用程序也是依照音讯的。那申明,应用程序是透过Windows来与用户通讯的,Windows又是选拔预约义的消息与应用程序通讯的。那一个新闻是带有种种音信的构造,应用程序和Windows使用这几个新闻决定下一步的操作。

譬如说:当用户用鼠标去点击一个windows应用程序的按钮的时候,windows操作系统就会捕获到那些点击按钮的动作,那一个时候它会基于捕获到的动作发送2个与之对应的预订义的音讯给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参数和再次来到值

事件最终会指向2个照旧多少个函数,函数要与事件所依据的嘱托匹配。事件所针对的函数(事件处理程序)的命名规则:遵照预订,事件处理程序应依据“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坐标,以及与事件有关的任何消息。

控件事件中,一般第3个参数都以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类型对象参数,3个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)跑起来,每隔一分钟触发一回事件

        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

    {

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

        }

 

        // 这么些艺术自然应该是把信息写入三个文本中

        // 那里我们用把音讯输出控制台代替

        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)、不管是多播委托依然单播委托,在尚未至极处理的景色下,在多少个线程的实践进程中去调用委托(委托对象所针对的函数),调用委托的推行是不会新起线程的,这些执行可能在原线程中的,这些对于事件也是千篇一律的。当然,假如是在委托所针对的函数里面去运营1个新的线程那正是其它三次事了。

(3)、事件是对准某一个实际的对象的,一般在该指标的所属类A中写好事件,并且写好触发事件的主意,那么这几个类A便是事件的发表者,然后在别的类B里面定义A的目的,并去开首化该对象的轩然大波,让事件指向B类中的某1个实际的艺术,B类正是A类事件的订阅者。当通过A类的指标来触发A类的轩然大波的时候(只好A类的对象来触发A类的事件,别的类的目的不能够触发A类的风云,只能订阅A类的轩然大波,即实例化A类的轩然大波),作为订阅者的B类会接收A类触发的事件,从而使得订阅函数被实施。三个发表者能够有三个订阅者,当发表者发送事件的时候,全数的订阅者都将接受到事件,从而执行订阅函数,可是即就是有八个订阅者也是单线程。

原版的书文链接:
14.1、委托 当要把办法作为实…

     然后大家定义2个主意。

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,它们一个是发送者,1个是源。

       }

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

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

上边我们用WPF来探望路由事件。

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

咱俩先是在XAML页面定义1个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。

     小编在Key伊芙ntArgs中又发现了质量

经过获取sender的项目,小编也足以观察,btnClild_Click的sender类型是Button,而btnParent_Click的sernder类型是RadioButton。

        public char KeyChar { get; }

事件驱动编程

     进一步表明了本身的答辩。上边我们来做三个看似的事例来扶助明白。

事件驱动编制程序这一个概念给自家的觉得很怪,因为直接用C#,而C#的浩大框架都以事件驱动的,所以向来以为事件驱动是当然。

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

而当事件驱动设计这么些词经常出现后,反而觉得蹊跷。

    拿我们地方做的例子来改。

就就像是,每一日吃籼米饭,突然有一天,全部人都说糙米饭好香的痛感一样,你一听就感觉蹊跷。

    
大家也定义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#框架中,大多是利用事件来展开多播的。

所以委托的多播和事件联合利用的框架,会招致选择那么些框架的初级开发者很多猜忌,而那种质疑,会产生许多不要求的题材。

诸如,
你定义了3个信托,另一个开发者用这么些委托做了个多播,当第5个开发者来爱抚那段代码时,假若她是新手,不精通委托的多播,那就很有恐怕只修改了寄托调用的代码。而从未去共同多播那么些委托的代码。那系统就生出了藏匿的bug。

那么,事件和嘱托到底是如何关系吧?

事件与寄托的确存在复杂的关联,怎么讲都以未可厚非的。但,C#开发者只须求牢记,他们俩无妨即可。在C#事件是事件,委托是委托。两者就就像int和string一样,没有其余关联。

缘由很简短,学习的进度中尽量下落概念混淆。而且,在C#付出中,好的架构者也司空见惯会将事件和寄托分离,所以,就认为事件和委托没有关联即可。

结语

实际上事件很好通晓,一点不复杂。作者在写那篇文章的经过中,也没悟出什么特其余或许说比较高档的用法。

但忠实的施用场景中,笔者的感觉到是,随着MVVM的成人,事件实际在被稳步放任。就算微软做了累累经典的事件驱动框架。但那都是过去了。

诸如WPF就算帮忙事件驱动,但MVVM在WPF下的显现堪称完美,所以WPF下的轩然大波几乎从未人用了。

再比如前端的Angularjs等框架,提供了上流的MVVM使用效益,也让新的前端设计师慢慢抛弃了事件。

为此,事件在以往的编程中,很大概将不在有那么重庆大学的地点了。但学好事件,对于大家知道微软框架,依然有非常大扶持的。

C#语法——元组类型

C#语法——泛型的有余使用

C#语法——await与async的不错打开情势

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

我对C#的认知。


注:此小说为原创,欢迎转发,请在小说页面显然地方给出此文链接!
若您认为那篇文章尚可,请点击下右下角的【推荐】,万分谢谢!
若是你觉得那篇小说对您抱有帮忙,这就无妨支付宝小小打赏一下吧。 

威尼斯人线上娱乐 4

 


相关文章

发表评论

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

网站地图xml地图