LINQ - Lambda表达式



术语“Lambda 表达式”来源于“lambda 演算”,后者是一种用于定义函数的数学记法。作为 LINQ 等式可执行部分的 Lambda 表达式,以运行时的方式转换逻辑,以便可以方便地传递给数据源。但是,Lambda 表达式的应用并不仅限于 LINQ。

这些表达式由以下语法表示:

(Input parameters) ⇒ Expression or statement block

这是一个 Lambda 表达式的示例:

y ⇒ y * y

上述表达式指定了一个名为 y 的参数,并且 y 的值被平方。但是,无法以这种形式执行 Lambda 表达式。下面显示了 C# 中 Lambda 表达式的示例。

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {

      delegate int del(int i);
      static void Main(string[] args) {

         del myDelegate = y ⇒ y * y;
         int j = myDelegate(5);
         Console.WriteLine(j);
         Console.ReadLine();
      }
   }
}

VB

Module Module1
   Private Delegate Function del(ByVal i As Integer) As Integer
   
   Sub Main(ByVal args As String())
   
      Dim myDelegate As del = Function(y) y * y
      Dim j As Integer = myDelegate(5)
      Console.WriteLine(j)
      Console.ReadLine()
	  
   End Sub
   
End Module

当编译并执行上述 C# 或 VB 代码时,将产生以下结果:

25

表达式 Lambda

由于上述 Lambda 表达式语法中的表达式位于右侧,因此它们也称为表达式 Lambda。

异步 Lambda

通过使用 async 关键字加入异步处理创建的 Lambda 表达式称为异步 Lambda。下面是一个异步 Lambda 的示例。

Func<Task<string>> getWordAsync = async()⇒ “hello”;

标准查询操作符中的 Lambda

查询操作符中的 Lambda 表达式由同一操作符按需计算,并持续作用于输入序列中的每个元素,而不是整个序列。Lambda 表达式允许开发人员将自己的逻辑输入到标准查询操作符中。在下面的示例中,开发人员使用了“Where”操作符,利用 Lambda 表达式从给定列表中提取奇数值。

C#

//Get the average of the odd Fibonacci numbers in the series... 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {     
      static void Main(string[] args) {
      
         int[] fibNum = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
         double averageValue = fibNum.Where(num ⇒ num % 2 == 1).Average();
         Console.WriteLine(averageValue);
         Console.ReadLine();
      }
   }
}

VB

Module Module1

   Sub Main()
   
      Dim fibNum As Integer() = {1, 1, 2, 3, 5, 8, 13, 21, 34}
      Dim averageValue As Double = fibNum.Where(Function(num) num Mod 2 = 1).Average()
	  
      Console.WriteLine(averageValue)
      Console.ReadLine()
	  
   End Sub
   
End Module

当编译并执行上述代码时,将产生以下结果:

7.33333333333333

Lambda 中的类型推断

在 C# 中,类型推断方便地用于各种情况下,而且无需显式指定类型。但是,对于 Lambda 表达式,类型推断仅在每个类型都已指定的情况下才有效,因为必须满足编译器的要求。让我们考虑以下示例。

delegate int Transformer (int i);

在这里,编译器使用类型推断来推断 x 是一个整数,这是通过检查 Transformer 的参数类型来完成的。

Lambda 表达式中的变量作用域

在 Lambda 表达式中使用变量作用域时,有一些规则,例如,在 Lambda 表达式中初始化的变量不能在外部方法中可见。还有一个规则是,除非引用它的委托有资格进行垃圾回收,否则捕获的变量不会被垃圾回收。此外,还有一条规则禁止 Lambda 表达式中的 return 语句导致封闭方法返回。

以下是一个演示 Lambda 表达式中变量作用域的示例。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {
      delegate bool D();
      delegate bool D2(int i);

      class Test {
         D del;
         D2 del2;
			
         public void TestMethod(int input) {
            int j = 0;
            // Initialize the delegates with lambda expressions.
            // Note access to 2 outer variables.
            // del will be invoked within this method.
            del = () ⇒ { j = 10; return j > input; };

            // del2 will be invoked after TestMethod goes out of scope.
            del2 = (x) ⇒ { return x == j; };

            // Demonstrate value of j:            
            // The delegate has not been invoked yet.
            Console.WriteLine("j = {0}", j);        // Invoke the delegate.
            bool boolResult = del();
           
            Console.WriteLine("j = {0}. b = {1}", j, boolResult);
         }

         static void Main() {
            Test test = new Test();
            test.TestMethod(5);

            // Prove that del2 still has a copy of
            // local variable j from TestMethod.
            bool result = test.del2(10);
           
            Console.WriteLine(result);

            Console.ReadKey();
         }
      }
   }
}

当编译并执行上述代码时,将产生以下结果:

j = 0
j = 10. b = True
True

表达式树

Lambda 表达式广泛用于表达式树的构建。表达式树将代码转换为类似于树的数据结构,其中每个节点本身都是一个表达式,例如方法调用或二元运算符,例如 x<y。以下是使用 Lambda 表达式构建表达式树的示例。

语句 Lambda

还存在语句 Lambda,它包含两到三个语句,但不用于构建表达式树。语句 Lambda 必须编写 return 语句。

语句 Lambda 的语法

(params)⇒ {statements}

语句 Lambda 的示例

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace lambdaexample {
   class Program {
      static void Main(string[] args) {
         int[] source = new[] { 3, 8, 4, 6, 1, 7, 9, 2, 4, 8 };

         foreach (int i in source.Where(x ⇒ 
            {
               if (x <= 3)
                  return true;
               else if (x >= 7)
                  return true;
               return false;
            }
         ))
        Console.WriteLine(i);
        Console.ReadLine();
      }
   }
}

当编译并执行上述代码时,将产生以下结果:

3
8
1
7
9
2
8

Lambda 用作基于方法的 LINQ 查询中的参数,并且不允许像匿名方法一样位于运算符(如isas)的左侧。尽管 Lambda 表达式与匿名方法非常相似,但它们并不局限于仅用作委托。

使用 Lambda 表达式时需要注意的几点

  • Lambda 表达式可以返回值,并且可以具有参数。

  • 可以使用多种方法定义 Lambda 表达式的参数。

  • 如果 Lambda 表达式中只有一个语句,则不需要花括号;如果有多个语句,则必须编写花括号和返回值。

  • 使用 Lambda 表达式,可以通过称为闭包的功能访问 Lambda 表达式块外部的变量。应谨慎使用闭包以避免任何问题。

  • 不可能在任何 Lambda 表达式内执行任何不安全代码。

  • Lambda 表达式不能用于运算符的左侧。

广告