visual C#(二十)分离应用程序逻辑并处理事件

参考书:《 visual C# 从入门到精通》
第三部分 用C#定义可扩展类型
第20章 分离应用程序逻辑并处理事件

20.1 理解委托

委托是对方法的引用。如:

Processor p=new Processor();
delegate ...performCalculationDelegate...;
performCalculationDelegate=p.performCalculation;
performCalculationDelegate();

应用程序通过委托来调用方法:performCalculationDelegate(),看起来就像是运行一个名为performCalculationDelegate的方法,但CLR知道它是委托。后面还可以更改委托引用的方法。委托可以一次引用多个方法。

20.1.1 .NET Framework类库的委托例子

第18章中,List<T>类的FindExists方法,它搜素集合然后返回匹配项或者测试匹配项是否存在,但我们事先是不知道怎么匹配的,匹配方式是要我们自己定义,通过谓词的形式指定匹配方式。这个谓词就是委托。

List<T>类中的AverageMaxCount和其他方法获取的参数实际上是泛型Func<T,TResult>委托,两个类型参数分别是传给委托的类型和返回值的类型,如int Thirties=personnel.Count(p=>p.Age>=30&&p.Age<=39);,其中Count方法期待的委托类型是Func<Person,int>。另外,还有Action委托类型,Action委托引用的是采取行动而不是返回值的方法,即void方法。

20.1.2 自动化工厂的例子

下面我们假定为一间自动化工厂写控制程序。工厂包含很多机器。我们的任务是将机器使用的不同的系统集成到一个控制程序中。为此,你需要提供在必要时快速关闭所有机器的一个机制。每台机器都有自己的方法来实现安全停机:

StopFolding();//折叠和切割机
FinishWelding();//焊接机
PaintOff();//彩印机

20.1.3 不用委托实现工厂控制系统

可以采用下面简单的方法实现停机功能:

class Controller{
    private FoldingMachine folder;
    private WeldingMachine welder;
    private PaintingMachine painter;
    ...;
    public void ShutDown(){
        folder.StopFolding();
        welder.FinishWelding();
        painter.PainrtOff();
    }
    ...;
}

这种方法是可行的,但扩展性和灵活性不好。Controller类是和机器绑定的,如果购入新机器,又需要修改这些代码了。

20.1.4 用委托实现工厂控制系统

首先像这样声明委托:

delegate void stopMachineryDelegate();

注意:

  • delegate来声明委托
  • 委托定义了它引用的方法的形式。指定返回类型、委托名称、和所有的参数

定义委托之后就可以创建它的实例了,并用+=操作符让该实例引用匹配的方法。如:

class Controller{
    delegate void stopMachineryDelegate();
    private stopMachineryDelegate stopMachinery;
    ...;
    public Controller(){
        this.stopMachinery+=folder.StopFolding;
    }
    ...;
}

上述代码将方法加到委托中,但还没有实际调用方法。这里的+操作已经重载了的,所以它具有特殊的含义。注意指定方法名是不要加圆括号和任何参数。

可以将+=用于任何未初始化的委托,该委托会自动初始化。也可以用new来显式初始化委托,让它引用一个特定的方法:

this.stopMachinery=new stopMachineryDelegate(floder.stopFolding);

然后可以通过调用委托来调用引用:

public void ShutDown(){
    this.stopMachinery();
}

委托的一个优势是它可以引用多个方法:

public Controller(){
    this.stopMachinery+=folder.StopFloding;
    this.stopMachinery+=welder.FinishWelding;
    this.stopMachinery+=painter.PaintOff;
}

调用this.stopMachinery()将自动依次调用这些方法。也可以用-=从委托中移除一个方法:

this.stopMachinery-=folder.StopFloding;

20.1.5 声明和使用委托

pass;

20.2 Lambda 表达式和委托

我们前面这样来向委托添加方法:this.stopMachinery+=folder.StopFolding;,但如果StopFolding方法的签名是:void StopFolding(int ShutDownTime);,这样我们就不能用同一个委托处理三个方法了。这时的一个解决方案是创建另一个方法:

void FinshFolding(){
    folder.StopFolding(0);
}

这样就可以将FinishFolding添加到委托中了。这是适配器的一个典型的例子。C#还提供了另一个方案,用Lambda表达式:this.stopMachinery+=(()=>folder.StopFolding(0));

20.3 启用事件通知

.NET Framework 提供了事件。可以定义并捕捉事件,并在事件发生时调用委托来处理。

20.3.1 声明事件

和声明字段相似,由于事件和委托一起使用,所以事件的类型必须是委托,且在声明前附加event前缀:event delegateTypeName eventName

如,对于自动化工厂的StopMachineryDelegate委托。它现在被转移到新类TemperatureMonitor温度监视器中:

class TemperatureMonitor{
    public delegate void StopMachineryDelegate();
    public event StopMachinery MachineOverheating;
    ...;
}

我们要把方法家倒事件中,即订阅事件或者**向事件登。

20.3.2 订阅事件

我们也是使用+=来订阅事件:

class TemperatureMonitor{
    public delegate void StopMachineryDelegate();
    public event StopMachinery MachineOverheating;
    ...;
}
...;
TemperatureMonitor tempMonitor=new TemperatureMonitor();
...;
tempMonitor.MachineOverheating+=()=>{folder.StopFolding(0);};
tempMonitor.MachineOverheating+=welder.Finishwelding;
tempMonitor.MachinerOverheating+=painter,paintOff;

20…3.3 取消订阅事件

-=来取消订阅事件。

20.3.4 引发事件

引发事件后,所有和事件关联的委托会被依次调用:

class TemperatureMonitor{
    public delegate void StopMachineryDelegate();
    public event StopMachinery MachineOverheating;
    ...;
    private void Notify(){
        if(this.MachineOverheating!=null){
            this.MachineOverheating();
        }
    }
    ...;
}

null检查是必要的,因为事件没有订阅方法的时候是null的,引发null的事件会抛出NullReferenceException异常。

20.4 理解用户界面事件

用于构造GUI.NET Framework类和控件广泛运用了事件。如从ButtonBase类派生的Button类继承了RoutedEventHandler类型的公共事件ClickRoutedEventHandler委托要求两个参数:一个是引发事件的对象的引用,另一个是EventArgs对象,它包含关于事件的额外信息:

public delegate void RoutedEventHandler(object sender,RoutedEventArgs e);

Button类的定义如下:

public class ButtonBase:...{
    public event RoutedEventHanderler Click;
    ...;
}
public class Button:ButtonBase{
    ...;
}

单击按钮,Button类将引发Click事件。

partial class MainPage:
	global::Windows.UI.Xaml.Controls.Page,
	global::Windows.UI.Xaml.Markup.IComponentConnector,
	global::Windows.UI.Xaml.Markup.IComponentConnector2{
        ...;
        public void Connect(int connectionId,object target){
            switch(conncetionId){
                    case 1:{
                        this.okay=global::Windows.UI.Xaml.Controls.Button)(target);
                        ...;
                        ((global::Windows.UI.Xaml.Controls.Button)this.okay).Click+=this.okayClick;
                        ...;
                    }
                    break;
                    default;
                    break;
            }
            this._contentLoaded=true;
        }
        ...;
    }

这些代码都是隐藏起来的,在VS中操作时他会自动生成,我们要做的其实只是写事件处理方法:

public sealed partial class MainPage:page{
    ...;
    private void okayClick(object sender,RoutedEventArgs args){
        //Click事件的处理
    }
}

实际上这些东西只有在真正的做一些项目时才能感受到它的价值的,一般情况下不一定用的上。下面例子强行用到这一章的委托和事件:

在VS中新建一个空白应用,在设计视图中将控件布局成这样:

在这里插入图片描述
比较麻烦的是需要放入四个GridView控件。MainPage.xaml.cs中的代码如下:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x804 上介绍了“空白页”项模板

namespace c_20_1_5
{
    /// <summary>
    /// 可用于自身或导航至 Frame 内部的空白页。
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
        public string n1 = "PowerDrill", n2 = "Hammer";
        public double price1 = 75.5, price2 = 18.35;
        public delegate void showMessage();
        public event showMessage showEvent;
        private showMessage showmess;
        public void addDelegate()
        {
            this.showmess += this.n1Show;
            this.showmess += this.n2Show;
        }
        private void powerDrillClick(object sender, RoutedEventArgs e)
        {
            this.quantity1++;
            updata();
        }
        
        private void Notify()
        {
            if (this.showEvent != null)
                this.showEvent();
        }
        private void HammerClick(object sender, RoutedEventArgs e)
        {
            this.quantity2++;
            updata();
        }

        private void CheckoutClick(object sender, RoutedEventArgs e)
        {
            message.Text = "";
            this.showmess = null;
            this.addDelegate();
            this.showmess();
            this.showEvent = null;
            this.showEvent += this.n1Show;
            this.showEvent += this.n2Show;
            this.Notify();

        }

        private void updata()
        {
            n1text.Text = $"{this.n1}--{this.quantity1}";
            n2_text.Text = $"{this.n2}--{this.quantity2}";
            total_text.Text = $"Total: {this.quantity1 * this.price1 + this.quantity2 * this.price2}";
        }
        private void n1Show()
        {
            message.Text += $"Item: {this.n1}, Quantity: {this.quantity1}, " +
                $"Money: {this.price1}*{this.quantity1}={this.quantity1 * this.price1}\n";
        }
        private void n2Show()
        {
            message.Text += $"Item: {this.n2}, Quantity: {this.quantity2}, " +
                $"Money: {this.price2}*{this.quantity2}={this.quantity2 * this.price2}\n";
        }
        public int quantity1 = 0, quantity2 = 0;
    }
}

运行效果如下:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值