zoukankan      html  css  js  c++  java
  • [.net 面向对象程序设计进阶] (5) Lamda表达式(一) 创建委托

    [.net 面向对象程序设计进阶] (5) Lamda表达式(一)  创建委托 

    本节导读: 

    通过学习Lambda表达式,学会创建委托和表达式目录树,深入了解Lambda的特性,让你的代码变的更加清晰、简洁、高效。 

    读前必备: 

    本节学习前,需要掌握以下知识: 

    A.泛型        (请参考[.net 面向对象编程基础]  (18) 泛型) 

    B.Linq基础 (请参照[.net 面向对象编程基础] (19) LINQ基础) 

    C.Linq使用  (请参照[.net 面向对象编程基础 (20) LINQ使用

    D.委托       (请参照[.net 面向对象编程基础]   (21) 委托) 

    E.事件      (请参照[.net 面向对象编程基础]   (22) 事件) 

    通过《.net 面向对象编程基础》系列中相关介绍,我们已经初步使用过了Lambda表达式进行Linq查询,这节我们主要深入了解Lambda表达式。

    1. 关于Lambda 

    Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数。 

    以上是微软对Lambda表达式的定义。从这个定义中,我们可以看出,Lambda的存在,主要做两件事: 

    A.创建委托(Delegate) 

    B.创建表达式树(Expression Tree) 

    此外,Lambda表达式的本质就是匿名方法(或叫匿名函数)。 

    后面面我们分别从这两个方法入手,进一步学习Lambda带我给我们的便利。 

    2. Lambda表达式 

    Lambda表达式的构成如下: 

    零个参数: ()=>expr 

    一个参数:(param)=>expr 或 param=>expr 

    多个参数:(param-list)=>expr 

    所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to" 

    当参数只有一个时,右边的括号可以省略。 

    下面是写法举例:

    //零个参数
    () => MethodName()
    //一个参数
    (x) => x+x
    X=>x+x
    //两个及以上参数
    (m,n) => m.Length>n
    //显式类型参数
    (int x,string y)=>y.Length>x

    上面的示例中,其中显式类型参数,是当编译器无法推断其参数类型时,可以显式的定义参数类型。

     3. Lambda语句

    Lambda语句和Lambda表达式类似,只是右边部分写在{}

    Lambda语句构成如下:

    (input parameters) => {statement;}

    示例:

    delegate void TestDelegate(string s);
    …
    TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
    myDel("Hello");

    4.异步Lambda(async和await)

    我们通过在《.net 面向对象编程基础》中学习,了解了事件本身也是一种特殊的委托,那么Lambda可用于创建委托也就是说同样可以来创建事件。

    下面看示例:

    首先看一个普通按钮点击事件的实现及Lambda语句写法

    下面是普通写法:

     this.myButton.Click += new System.EventHandler(this.myButton_Click);
    
    //事件通常委托写法
    private void myButton_Click(object sender, EventArgs e)
    {
        MessageBox.Show("您好,我是按钮点击事件!");
    }

    下面是Lambda写法:

    //事件Lambda语句写法
    myButton.Click += (sender, e) => { MessageBox.Show("您好,我是按钮点击事件!这是Lambda语句写法"); };

    上面两种写法,我们在前面的章节中已经有很多例子了。

    下面我们看一下异步的事件:

    //异步事件普通写法
    private async void asyncButton_Click(object sender, EventArgs e)
    {
        await MyMethodAsync();
        MessageBox.Show("您好,我是按钮点击事件!异步的哟!");
    
    }
    async Task MyMethodAsync()
    {            
        await Task.Delay(1000);
    }

    我们用Lambda来改写上面的异步事件:

    //异步事件Lambda写法
    asyncButton.Click += async (sender, e) => { await MyMethodAsync();  MessageBox.Show("您好,我是按钮点击事件!异步的哟!这是Lambda语句写法"); };
    async Task MyMethodAsync()
    {            
        await Task.Delay(1000);
    }

    上面的异步事件,就是在事件委托阶段使用async来表示这是一个异步的,在事件处理中阶段使用await关键词来指定一个异步方法。

    Lambda语句写法同样是针对异步事件的简洁写法,具有相同的效力。

    5. 标准查询运算的Lambda表达式

    5.1泛型委托 使用Lambda

    我们在[.net 面向对象编程基础]   (21) 委托一节中说到了三种常用的泛型委托

    Action(无返回值泛型委托)

    Func(有返回值泛型委托)

    predicate(返回值为bool型的泛型委托)

    这节不再重复说明泛型委托,不熟悉泛型委托的小伙伴,请参考,我们只举例说明泛型委托和它的Lambda写法

    //Action 无返回值类型的 泛型委托
    //匿名方法声明及调用
    Action<int, int> act = delegate (int a, int b) {
        Console.WriteLine(a + "+" + b + "=" + (a + b));
    };
    act(11, 22);
    //表达式声明及调用
    Action<int, int> actLambda = (a, b) => { Console.WriteLine(a + "+" + b + "=" + (a + b)); };
    actLambda(111, 222);
    
    //Func 带返回值的 泛型委托
    //匿名方法声明及调用
    Func<int, int, string> acc = delegate (int a, int b) {
        return (a + "+" + b + "=" + (a + b));
    };
    Console.WriteLine(acc(11, 22));
    //表达式声明及调用
    Func<int, int, string> ac = (a, b) => { return (a + "+" + b + "=" + (a + b)); };
    Console.WriteLine(ac(111, 222));

    5.2 Linq中使用Lambda

    关于在Linq中使用Lambda表达式,我们在[.net 面向对象编程基础 (20) LINQ使用有详细说明了,不熟悉的小伙伴请参考,下面我们举例说明几种常用的Lambda查询写法。

    5.2.1 Count 求数量

    //查询下列数中的奇数 - Lambda写法
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    int oddNumbers = numbers.Count(n => n % 2 == 1);
    Console.WriteLine("奇数有:"+oddNumbers.ToString()+ "个,Lambda写法");
    //查询下列数中的奇数 -Linq查询写法
    var oddNumbersFunc = (from num in numbers
                            where num % 2 == 1
                            select num
                        ).Count();
    
    Console.WriteLine("奇数有:" + oddNumbersFunc.ToString() + "个,Linq查询写法");

    运行结果如下:

    5.2.2  TakeWhile 满足条件就返回集合

    //TakeWhile只要满足指定的条件就返回,在检检到number中的6时,就返回,其中8 和9 不满足条件,则返回 5 4 1 3 
    int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
    var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
    Console.WriteLine("满足条件就返回的数有:" + firstNumbersLessThan6.Count().ToString() + "个,Lambda写法");
    //第二个参数index表示n的索引位置
    string NewNumber = String.Empty;
    numbers.TakeWhile((n, index) => n >= index).ToList().ForEach(m=> NewNumber+=m +" ");
    Console.WriteLine("满足条件就返回的数有:" + NewNumber + ",Lambda写法");

     运行结果如下:

    6. Lambda的类型推理

    在编写 lambda 时,通常不必为输入参数指定类型,因为编译器可以根据 lambda 主体、参数的委托类型以及 C# 语言规范中描述的其他因素来推断类型。 对于大多数标准查询运算符,第一个输入是源序列中的元素类型。 因此,如果要查询 IEnumerable<Customer>,则输入变量将被推断为 Customer 对象,这意味着你可以访问其方法和属性:

    //Lambda的类型推理
    List<MyClass> list = new List<MyClass>() {
        new MyClass(){  at1 = "aaa", at2 = 2, at3 = DateTime.Now},
        new MyClass{ at1 = "bbb", at2 = 5, at3 = DateTime.Parse("2015-06-07") },
        new MyClass{ at1 = "aaa", at2 = 1, at3 = DateTime.Parse("2010-11-12")  }
    };            
    Console.WriteLine("at1为aaa的元素有:" + list.Where(m => m.at1 == "aaa").Count() + "个,Lambda写法"); 
    class MyClass {
        public  string at1 { get; set; }
        public int at2 { get; set; }
        public DateTime at3 { get; set; }
    }

    运行结果如下:

    Lambda 的一般规则如下:

    A. Lambda 包含的参数数量必须与委托类型包含的参数数量相同。

    B. Lambda 中的每个输入参数必须都能够隐式转换为其对应的委托参数。

    C. Lambda 的返回值(如果有)必须能够隐式转换为委托的返回类型。

    请注意,lambda 表达式本身没有类型,因为常规类型系统没有“Lambda 表达式”这一内部概念。但是,有时以一种非正式的方式谈论 lambda 表达式的“类型”会很方便。 在这些情况下,类型是指委托类型或 lambda 表达式所转换到的 Expression 类型。

    7. Lambda表达式的变量范围

    我们在[.net 面向对象编程基础]  (19) LINQ基础 一节中说到匿名方法时提到匿名方法可以引用满园内的外部变量,前面示例中也有提及。

    下面我们看一个示例: 

    class Program
    {
        static int num = 2;
        static void Main(string[] args)
        {
            int num2 = 7;
    
            //Lambda表达式的变量范围
            //相对以下表达式内部,一个类的字段num和一个变量num2,下面测试在表达式内部调用
            int[] numbers2 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
            //我们对大于num小于num2的数求和
            int sum = numbers2.Where(m => m > num && m < num2).Sum();
            Console.WriteLine("大于num小于num2的和为:" + sum);
            Console.ReadKey();
        }
    }

    下列规则适用于 lambda 表达式中的变量范围:

    A.捕获的变量将不会被作为垃圾回收,直至引用变量的委托符合垃圾回收的条件。

    B.在外部方法中看不到 lambda 表达式内引入的变量。

    C.Lambda 表达式无法从封闭方法中直接捕获 ref 或 out 参数。

    D.Lambda 表达式中的返回语句不会导致封闭方法返回。

    E.如果跳转语句的目标在块外部,则 lambda 表达式不能包含位于 lambda 函数内部的 goto 语句、break 语句或 continue 语句。 同样,如果目标在块内部,则在 lambda 函数块外部使用跳转语句也是错误的。

    8.要点:

    本节主要说明了:

    Lambda表达式在创建委托中的应用;

    Lambda表达式在Linq查询中的应用;

    Lambda 语句在异步事件中的应用;

    Lambda 的两个特性:类型推理和外部变量引用

    下一节,我们主要说明Lambda的另一个特点,就是创建表达式目录树(Expression Tree)。

    ==============================================================================================  

     返回目录

     <如果对你有帮助,记得点一下推荐哦,如有

    有不明白或错误之处,请多交流>  

    <对本系列文章阅读有困难的朋友,请先看《.net 面向对象编程基础》>

    <转载声明:技术需要共享精神,欢迎转载本博客中的文章,但请注明版权及URL>

    .NET 技术交流群:467189533    .NET 程序设计

    ==============================================================================================   

  • 相关阅读:
    011 Go语言基础之map
    009 Go语言基础之切片
    计算机组成原理 — GPU 图形处理器
    js 实现各浏览器全屏
    js 实现各浏览器全屏
    IIS-真正解决ASP.NET每一个页面首次访问超级慢的问题
    IIS-真正解决ASP.NET每一个页面首次访问超级慢的问题
    首次启动速度慢的问题记录IIS性能优化
    首次启动速度慢的问题记录IIS性能优化
    C#-执行cmd命令,获取结果
  • 原文地址:https://www.cnblogs.com/yubinfeng/p/4619495.html
Copyright © 2011-2022 走看看