Một biểu thức lambda là một hàm nặc danh (anonymous function) dùng để tạo các kiểu delegates hay cây biểu thức (expression tree). Bằng cách dùng biểu thức lambda, chúng ta có thể viết các hàm cục bộ có thể được chuyển như các tham số hay được trả về như giá trị của hàm gọi. Biểu thức lambda rất hữu ích cho việc viết các biểu thức truy vấn LINQ.

Cú pháp của biểu thức lambda:


(tham số đầu vào) => biểu thức hay khối lệnh

Ví dụ biểu thức lambda y => y*y xác định tham số tên y và trả về giá trị là y bình phương.

Có thể gán biểu thức lambda y => y*y đến kiểu delegate.

Biểu diễn trong C#:


delegate int del(int i);

static void Main(string[] args)

{

  del myDelegate = y => y * y;

  int j = myDelegate(5);

  Console.WriteLine(j); // 25

  Console.ReadLine();

}

Biểu diễn trong VB:


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

Expression tree

Một expression tree thể hiện code trong một cấu trúc dữ liệu trông giống như dạng cây với bản thân các node là một biểu thức có thể là một phương thức hay một toán tử nhị phân như x > y. Biểu thức lambda được dùng trong việc xây dựng expression tree. Ví dụ dùng x => x*x để tạo expression tree:

Mã C#


Expression<del> myET = x => x * x;

Mã VB


Dim myET As Expression(Of del) = Function(x) x * x

Expression lambda

Trong ví dụ về biểu thức lambda (lambda expression) y => y*y, biểu thức y*y còn được gọi expression lambda. Expression lambda được dùng trong xây dựng các cây biểu thức. expression lambda trả về kết quả của biểu thức và có cú pháp (trong C#) như sau:


(danh sách tham số đầu vào) => biểu thức

Dấu ngoặc đơn được yêu cầu khi có từ hai tham số đầu vào trở lên. Ví dụ (C#):


(x, y) => x == y

Trong trường hợp trình biên dịch gặp khó khăn trong việc suy diễn kiểu thì cần chỉ ra kiểu một cách tường minh cho các tham số. Ví dụ (C#):


(int x, string s) => s.Length > x

Thỉnh thoảng danh sách tham số là rỗng:


() => SomeMethod()

Lưu ý rằng, expression lambda trong ví dụ trên là một phương thức và bạn chỉ có thể dùng các phương thức gọi trong biểu thức lambda trong phạm vi .NET Framework, vượt quá phạm vi, ví dụ dùng trong SQL Server, thì phương thức gọi không có ý nghĩa.

Statement lambda

Một statement lambda trông giống như một biểu thức lambda ngoại trừ các lệnh sẽ được đặt trong dấu ngoặc nhọn ({}) như cú pháp sau:


(danh sách tham số đầu vào) => {statement;}

Ví dụ về statement lambda trong C#:


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 (x >= 7)

       return true;

     return false;

   }

))

Console.WriteLine(i);

Console.ReadLine();

Kết quả:


3

8

1

7

9

2

8

Statement lambda không được dùng để tạo expression tree.

Async lambda

Biểu thức lambda được tạo bởi quá trình không đồng bộ bằng cách dùng từ khoá async được gọi là async lambda. Ví dụ:


Func<Task<string>> getWordAsync = async() => "hello";

Suy diễn kiểu trong lambda

Trong C#, suy diễn kiểu là một chức năng hữu ích trong trường hợp kiểu không được xác định một cách tường minh. Với biểu thức lambda, suy diễn kiểu chỉ làm việc khi mỗi kiểu đã được xác định như trình biên dịch phải được thoả mãn. Bản thân biểu thức lambda không có quan niệm “kiểu”.

Phạm vi biến trong biểu thức lambda

Phạm vi biến (variable scope) trong biểu thức lambda tuân theo một vài luật sau:

  • Một biến được nắm bắt sẽ không được đưa vào garbage collection (cơ chế quản lý bộ nhớ) cho đến khi delegate tham chiếu nó trở nên đủ điều kiện cho garbage collection.
  • Một biến khai báo trong biểu thức lambda sẽ không được nhìn thấy bởi các phương thức bên ngoài.
  • Một biểu thức lambda sẽ không thể nắm bắt trực tiếp một tham số ref hay out từ một phương thức kèm theo.
  • Một lệnh trả về trong một biểu thức lambda sẽ không phát sinh phương thức kèm theo để trả về.
  • Một biểu thức lamda sẽ không chứa lệnh goto, lệnh break, hay lệnh continue trong khối hàm lamda nếu đích đến của các lệnh ở bên ngoài khối hàm lambda. Cũng sẽ phát sinh lỗi nếu đích đến của một lệnh nhảy bên ngoài khối hàm lambda là bên trong khối hàm lambda.

Ví dụ (C#):


delegate bool D();

delegate bool D2(int i);

class Test

{

  D del;

  D2 del2;

  public void TestMethod(int input)

  {

   int j = 0;

   // khởi tạo các delegates với biểu thức lambda.

   // Chú ý truy cập đến hai biến ngoài.

   // del sẽ được gọi với phương thức này

   del = () => { j = 10;  return j > input; };

   // del2 sẽ được gọi khi TestMethod vượt khỏi phạm vi.

   del2 = (x) => {return x == j; };

   // Minh hoạ giá trị của j:

   // Kết quả: j = 0

   // Delegate chưa được gọi.

   Console.WriteLine("j = {0}", j);// gọi delegate.

   bool boolResult = del();

   // Kết quả: j = 10 b = True

   Console.WriteLine("j = {0}. b = {1}", j, boolResult);

  }

  static void Main()

  {

   Test test = new Test();

   test.TestMethod(5);

   // del2 có một bản sao của

   // biến j từ TestMethod.

   bool result = test.del2(10);

   // Kết quả: True

   Console.WriteLine(result);

   Console.ReadKey();

  }

}

Biểu thức lambda với các toán tử truy vấn chuẩn

Một biểu thức lambda với một toán tử truy vấn được định giá bởi cùng yêu cầu và làm việc trên mỗi phần tử trong chuỗi đầu vào, không phải trên toàn bộ chuỗi. Các nhà phát triển được phép bằng biểu thức lambda để đưa logic của riêng họ vào trong các toán tử truy vấn chuẩn. Trong ví dụ sau đây chúng ta dùng toán tử ‘Where’ để lấy các giá trị lẻ từ danh sách bằng cách dùng biểu thức lambda:

Mã C#


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();

Mã VB


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()

< Học LINQ