第四章 管理程序流(In .net4.5) 之 事件和回调

1. 概述

  本章讲解如何使用 委托、lambda表达式 和 匿名方法 来创建和使用事件。

2. 主要内容

  2.1 理解委托

    委托是一种用方法签名形式定义的类型。可以让它指向其他方法,可以通过它调用其他方法。

    ① 委托支持多播(multicasting),可以用 + 和 += 操作符绑定多个方法到委托中(用 - 和 -= 取消绑定)。

public void Method1() {}
public void Method2() {}

public delegate void Del();

public void Multicast()
{
    Del d = Method1;
    d += Method2;

    d();
}

    ② 需要绑定到委托的方法,签名不一定非得和委托完全一致。有两种特殊情况:

      Covariance:如果方法的返回值类型派生自委托的返回值类型,则该方法可以绑定到该委托。

public delegate TextWriter CovarianceDel();

public StreamWriter MethodStream() { return null; }
public StringWriter MethodString() { return null; }

CovarianceDel del = MethodStream;
del = MethodSting;

      Contravariance:如果委托的参数类型派生自方法的参数类型,则该方法可以绑定到该委托。

void DoSomething(TextWriter tw) { }
public Delegate void Contravariance(StreamWriter sw);

Contravariance = DoSomething;

  2.2 使用 Lambda 表达式

    Lambda表达式是匿名方法的升级版。

Calculate calc = (x, y) => x + y;
Console.WriteLine(calc(3, 4)); //Displays 7

    有时候为某些事件声明专用的委托类型会感觉比较笨拙,鉴于此,系统内建了两个委托类型来简化开发    

    Func<...> : 支持0-16个参数,一个返回值。

    Action<...>: 支持0-16个参数,无返回值。

Action<int, int> calc = (x, y) =>
{
    Console.WriteLine(x + y);
};

calc(3, 4); // displays 7

    *闭包(closure):如果一个委托引用了一个本地变量,并且被返回给了调用方。这时委托就具备了比该变量更长的作用域。

      为了避免这种情况,编译器会生成相应的代码去保证该变量具有和委托一样长的声明周期。(纯翻译,不知道理解的对不对。。)

  2.3 使用事件

    订阅和发布 是 一种流行的设计模式,用于实现同类问题的重用方案。

public class Pub
{
    public Action OnChange { get; set; }

    public void Raise()
    {
        if (OnChange != null)
            OnChange();
    }
}

public void CreateAndRaise()
{
    Pub p = new Pub();
    p.OnChange += () =>   Console.WriteLine("Raise method1");
    p.OnChange += () =>   Console.WriteLine("Raise method2");
    p.Raise();
}

    上述代码使用委托实现的订阅机制,有以下问题:

    ① 如果把method2订阅时的 += 换成 =,就会覆盖掉前面method1的订阅。

    ② OnChange是公共属性,外部用户可以随意调用。

    为了避免上述问题,C#推出了 event 关键字。

public class Pub
{
    public event Action OnChange = delegate { };

    public void Raise()
        OnChange();
}

    上述代码中的Action可以换成EventHandler<T>委托,可以用于传递数据。

    下面是包含异常处理的代码

public class Pub
{
    public event EventHandler OnChange = delegate {};
    public void Raise()
    {
        var exceptions = new List<Exception>();

        foreach(Delegate handler in OnChange.GetInvocationList())
        {
            try
            {
                 handler.DynamicInvoke(this, EventArgs.Empty);
            }
            catch(Exception ex)
            {
                 exceptions.Add(ex);
            }
        }

        if (exceptions.Any())
            throw new AggregatedException(exceptions);
    }
}

public void CreateAndRaise()
{
    Pub p = new Pub();

    p.OnChange += (sender, e) =>Console.WriteLine("1 called");
    p.OnChange += (sender, e) =>{ throw new Exception();};
    p.OnChange += (sender, e) =>Console.WriteLine("3 called");

    try
    {
        p.Raise();
    }
    catch(AggregatedException ex)
    {
        Console.WriteLine(ex.InnerExceptions.Count);
    }
}

3. 总结

  ① 委托是一个具有方法签名方式的类型,可以包含一个指向方法的引用。

  ② 委托可以被实例化,传递 和 触发。

  ③ Lambda表达式使用 => 操作符来创建匿名方法。

  ④ event是基于委托机制,高于委托机制的语法糖,可以方便的实现发布-订阅机制。

  ⑤ event只能在声明他的类内被触发。event使用者只能删除和添加invocation列表的方法。

  ⑥ 可以自定义事件访问器或者直接使用内建的委托类型。

时间: 2024-08-28 09:23:16

第四章 管理程序流(In .net4.5) 之 事件和回调的相关文章

第三章 管理程序流(In .net4.5) 之 实现程序流

1. 概述 本章内容包括 布尔表达式.流控制方式.集合遍历 以及 流跳转. 2. 主要内容 *由于该章内容比较基础,日常用的也很多,故对一些常用的基础内容不再赘述. 2.1 使用布尔表达式 熟悉下列比较运算符:>, <, >=, <=, ==, !=. 熟悉下列逻辑表达式:&&, ||, ^. bool a = true; bool b = false; Console.WriteLine(a ^ a); //false Console.WriteLine(a ^

第五章 管理程序流(In .net4.5) 之 异常处理

1. 概述 本章包括.net4.5中异常处理相关的部分. 2. 主要内容 2.1 处理异常 ① try.cahtch.finally 机制,无需多言. ② 使用 Environment.FailFast 方法,可以立即终止程序,并写入系统事件日志.会绕过finally的执行. public static void Main() { string s = Console.ReadLine(); try { int i = int.Parse(s); if (i == 42) Environment

第二章 管理程序流(In .net4.5) 之 管理多线程

1. 概述 本章包括同步资源以及取消长时间任务相关的内容. 2. 主要内容 2.1 同步资源 ① lock关键字实现.会阻塞程序,有可能会导致死锁. ② volatile关键字可以禁用编译优化,用于避免优化代码时对多线程的影响. private static volatile int _flag = 0; ③ Interlocked关键字可以原子化一些简单数字运算和比较替换操作. Interlocked.Increment(ref n); // Interlocked.Decrement(ref

第一章 管理程序流 之 实现多线程和异步处理

1. 概述 本章主要讲解.net4.5如何实现多线程和异步处理的相关内容. 2. 主要内容 2.1 理解线程 ① 使用Thread类   public static class Program   {       public static void ThreadMethod()       {           for (int i = 0; i < 10; i++)           {               Console.WriteLine(“ThreadProc: {0}”,

第四章 初步进入linux世界

第四章 初步进入linux世界 [Linux 系统启动过程] Linux的启动其实和windows的启动过程很类似,不过windows我们是无法看到启动信息的,而linux启动时我们会看到许多启动信息,例如某个服务是否启动. Linux系统的启动过程大体上可分为五部分:内核的引导:运行init:系统初始化:建立终端 :用户登录系统. A 内核引导 当计算机打开电源后,首先是BIOS开机自检,按照BIOS中设置的启动设备(通常是硬盘)来启动.紧接着由启动设备上的grub程序开始引导linux,当引

2017上半年软考 第四章 重要知识点

第四章 讲了项目管理的一般知识,重点是:[项目特点:一次性,临时性,独特性.渐进明细: 信息系统集成项目特点:以满足客户和用户的需求为根本出发点.是选择最适合的用户需求和投资规模的产品.高技术和高技术集成.系统工程.成员年轻流动率高.强调沟通重要性,系统集成项目管理即是一种管理行为又是一种技术行为: 项目的约束性目标=管理性目标: 项目成果性目标=项目目标: 项目smart原则:具体的specific.可测量的measurable.可达到的attainable.相关的relevant.有明确时限

CSS3秘笈复习:十三章&amp;十四章&amp;十五章&amp;十六章&amp;十七章

第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮动元素.关键字:left.right或both. (2).浮动外围元素:让包含浮动元素的<div>也浮动.选择这种方法一定要在浮动容器后面的任何元素中添加一个clear属性,确保浮动元素落到容器的下方. (3).利用overflow : hidden.另一种常见的方法是在外围的样式中添加以下属性:

现代软件工程讨论第一章-第四章

第一章 1.代码如下 #include <iostream> #include <cstdio> #include <time.h> using namespace std; int main(){ srand(time(0)); while(1){ printf("随机生成的一个小学四则运算题目,除法省去余数\n"); int num1 = rand() % 10; int num2 = rand() % 10; int index = rand(

第26章 最大流(正在修改)

一.综述 1.定义 定义1:流网络 定义2:残留容量 定义3:增广路径 已知一个网络流G=(V,E)和流f,增广路径p为残留网络G|f中从s到t的一条简单路径 能够沿一条增广路径p的每条边传输的网络流的最大量为p的残留容量,由下式定义: c|f(p) = min{c|f(u,v) : (u,v)在p上} 定义4:割.净流.容量.最小割 净流和容量的区别: 穿过(S,T)的净流由双向的正网络流组成:在加上从S到T的正网络流的同时,减去从T到S的正网络流. 割(S,T)的容量仅由从S到T的连计算而得