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的区别,是我们必须要知道的。

 

原文地址:https://www.cnblogs.com/MoRanQianXiao/p/7772234.html