• c#之Lambda表达式


    Lambda表达式

    前言:我们可以使用 一个新语法把实现的代码赋予委托:Lambda表达式。只要有委托参数类型的地方就可以使用Lambda表达式。我们把我们上一篇博客中的例子改为Lambda表达式。

    完整的代码如下:

     

     

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _15匿名方法
    {
        class Program
        {
            static void Main(string[] args)
            {
                string mid = ",我是中间的,";
                Func<string, string> lambda = param =>
              {
                  param += mid;
                  param += "我是最后的";
                  return param;
              };
                Console.WriteLine(lambda("我是最前面的"));
                Console.ReadKey();
            }
        }
    }

    代码的显示如下:

    Lambda表达式有几种定义参数的方式。“=>”的左边列出了需要的参数,Lambda运算符的右边定义了赋予Lambda变量的方法的实现的代码。

    一:参数:

    Lambda表达式有几种定义参数的方式,如果只有一个参数的话,只写出参数名就可以了。下面的Lambda表达式使用了参数s。因为委托定义了一个string参数,所以s的类型就是string类型。实现代码调用String.Format();方法来返回一个字符串,在调用该委托的时候,就把字符串写在控制台上。

    Func<string, string> oneParam = s => string.Format("转换为大写的形式 :{0}", s.ToUpper());
    Console.WriteLine(oneParam("hao"));

    截图如下:

    如果使用多个参数的时候,就把参数名放在花括号中,这里参数x和y都是double类型,由Func<double,double,double>委托定义:

    private static Func<double, double, double> _twoParams = (x, y) => x * y;

    方法的调用:

    Console.WriteLine(_twoParams(3, 2));

    我们可以不在花括号中添加参数的类型,因为编译器会自动的帮助我们匹配参数的类型。

    二:多行代码:

    如果Lambda表达式中只有一条语句的话,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐式的return语句。比如我们看下面的一行代码:

    private static Func<double, double> squate = x => x * x;

    但是添加或括号和return语句也是完全合法的,但是添加这些符号不容易阅读,添加之后如下:

    private static Func<double, double> squate = x => { return x * x; };

    但是在Lambda表达式中如果想实现多条语句的时候,就必须加上花括号和return;

               Func<string, string> anonDel = param =>
                {
                    param += mid;
                    param += "and this was added to the string";
                    return param;
                };

    三:闭包:

    通过Lambda表达式可以访问Lambda表达式外部的变量。这称为闭包. 闭包是一个非常好的功能,但是如果使用不当的话,也会非常的危险,我们首先看一个例子:

    int someVal = 5;
    Func<int, int> f = x => x + someVal;

    假设不假设在调用f时,Lambda表达式创建了一个以后使用的新方法,这似乎没有什么问题,我们看看我们上面写的代码,调用f的返回值应该是x+5的结果。但似乎不是这样,假定我们要修改someVal,于是调用Lambda表达式时,会使用someVal的新值。调用f(3)的结果是10;

    特别是,当通过另一个线程调用Lambda表达式的时候,我们可能不知道进行了这个调用,也不知道外部的变量的当前值是什么?那我们Lambda表达式在访问外部的变量的时候,对于Lambda :x=>x + someVal ,编译器会创建一个匿名类,它有一个构造函数来传递外部的变量。该构造函数取决于从外部传递进来的变量个数。对于这个简单的例子。构造函数会接收一个int。匿名类包含一个匿名的方法。完整的代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _16Lambda表达式
    {
        /// <summary>
        /// 匿名类
        /// </summary>
        public class AnonymousClass
        {
    
            private int _someVal;
            public int SomeVal
            {
                get { return _someVal; }
                set { _someVal = value; }
            }
    
            /// <summary>
            /// 构造函数初始化赋值操作
            /// </summary>
            /// <param name="someVal">外部传递的值</param>
            public AnonymousClass(int someVal)
            {
                this.SomeVal = someVal;
            }
    
            /// <summary>
            /// 匿名方法
            /// </summary>
            /// <param name="x"></param>
            /// <returns></returns>
            public int AnonymousMathod(int x)
            {
                return x + _someVal;
            }
    
        }
    }

    使用Lambda表达式并调用该方法,会创建一个匿名类的实例,并传递调用该方法时的变量。

    四:使用foreach语句的闭包:

    我们先看代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _16Lambda表达式
    {
        class Program
        {
            static void Main(string[] args)
            {
                //泛型集合
                List<int> values = new List<int>() { 10, 20, 30 };
                //泛型集合中存储一个委托类型的方法(有返回值无参数)
                List<Func<int>> funcs = new List<Func<int>>();
                //遍历values存储到集合中
                //在集合中添加的对象是没有参数的但是返回values集合中的每一个值
                foreach (var val in values)
                {
                    funcs.Add(() => val);
                }
                //遍历集合删除所有的方法  f():无参数有一个int类型的值
                foreach (var f in funcs)
                {
                    Console.WriteLine(f());
                }
                Console.ReadKey();
            }
        }
    }

    在上面的例子中,首先用10 20 30 填充了一个名为values的列表,变量funcs引用了一个泛型列表,其中的每一个对象都引用Func<int>类型的委托。第一条foreach语句添加了funcs列表中的每一个元素。添加到项中的函数使用Lambda表达式定义。该Lambda表达式使用了一个变量val,该变量在Lambda表达式的外部定义为foreach语句的循环变量。第二条foreach语句迭代func列表,已调用列表中引用的每个函数。结果如下:

    在c#5.0中,这段代码发生了变化,但是使用c#4或者是更早的版本的时候,会在控制台中输出3次30,在第一个foreach循环中国使用闭包时,所创建的函数是在调用,而不是在迭代是获得val的值。编译器会从foreach语中创建一个while循环。在c#4中,编译器在while循环外部定义循环变量。在每次迭代是重用这个变量。因此,在循环结束的时候,该变量的值就是最后一次迭代的值。要想在使用c#4时,让代码的结果为10 20 30,必须将代码改为使用一个局部的变量。并将这个局部的变量传入Lambda表达式。这样的话,每次迭代的值将保留一个不同的值。完整的代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace _16Lambda表达式
    {
        class Program
        {
            static void Main(string[] args)
            {
                //泛型集合
                List<int> values = new List<int>() { 10, 20, 30 };
                //泛型集合中存储一个委托类型的方法(有返回值无参数)
                List<Func<int>> funcs = new List<Func<int>>();
                //遍历values存储到集合中
                //在集合中添加的对象是没有参数的但是返回values集合中的每一个值
                foreach (var val in values)
                {
                    var v = val;
                    funcs.Add(() => v);
                }
                //遍历集合删除所有的方法  f():无参数有一个int类型
                foreach (var f in funcs)
                {
                    Console.WriteLine(f());
                }
                Console.ReadKey();
            }
        }
    }

    在c#5.0中,不在需要做这种代码的修改了(即将代码修改为局部的变量)。在c#5.0会在while循环的代码块中创建一个不同的局部循环变量。所以值会自动的得到保存。这是c#4.0和c#5.0的区别,是我们必须要知道的。

     

  • 相关阅读:
    方法重载与方法重写的概念和区别(转载)
    sql exist 和not exist(转载)
    SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON 什么意思 sql server 2005 2008
    sql中的isnull
    sql中unique和distinct
    SQLServer中的Merge使用
    SQL Server系统表sysobjects介绍与使用
    sql select as
    存储过程SET XACT_ABORT ON
    SQL Server之存储过程基础知识
  • 原文地址:https://www.cnblogs.com/MoRanQianXiao/p/7772234.html
Copyright © 2020-2023  润新知