Linq 简明教程

LINQ - Lambda Expressions

术语“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

Expression Lambda

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

Async Lambdas

通过使用 async 关键字结合异步处理创建的 lambda 表达式称为异步 lambda。以下是一个异步 lambda 示例。

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

Lambda in Standard Query Operators

查询运算符中的 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

Type Inference in Lambda

在 C# 中,类型推断在各种情况下得到了方便的使用,甚至无需显式指定类型。然而,在 lambda 表达式的情况下,类型推断仅在编译器必须满足时才起作用,前提是每个类型都已指定。我们来看一下下面的示例。

delegate int Transformer (int i);

这里,编译器利用类型推断根据 Transformer 的参数类型来利用 x 是一个整数这一事实。

Variable Scope in Lambda Expression

在 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

Expression Tree

Lambda 表达式广泛用于 Expression Tree 构造。表达式树将代码赠送给类似树的数据结构,其中每个节点本身就是表达式,比如方法调用或二进制运算,比如 x<y。以下是一个用于构造表达式树的 lambda 表达式使用示例。

Statement Lambda

还有包含两个或三个语句的 statement lambdas ,但未用于构造表达式树。必须在语句 lambda 中编写 return 语句。

语句 lambda 的语法

(params)⇒ {statements}

Example of a statement 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 表达式很像匿名方法,但它们完全不受限于仅用作委托。

Points to remember while using lambda expressions

  1. Lambda 表达式可以返回值并且可能有参数。

  2. 可以通过多种方式在 lambda 表达式中定义参数。

  3. 如果 lambda 表达式中只有一条语句,则不需要花括号,但如果有多条语句,则必须编写花括号以及返回值。

  4. 借助一个称为闭包的特性,lambda 表达式可以访问 lambda 表达式块外部存在的变量。应谨慎使用闭包,以避免出现任何问题。

  5. 不可能在任何 lambda 表达式中执行任何不安全代码。

  6. Lambda 表达式不得用于运算符的左侧。