Func vs Predicate vs Expression trong C# .Net

Func vs Predicate vs Expression trong C# .Net

April 18, 2025 0 By Nam Vu

Trong C#, delegates cho phép bạn xử lý các phương thức như biến, có thể truyền chúng hoặc gán chúng cho các phương thức khác. Khi tìm hiểu về delegates, bạn có thể đã gặp custom delegates, nơi bạn định nghĩa các loại delegate riêng. Nếu bạn chưa nắm rõ về cơ bản của delegates, tôi khuyên bạn nên tham khảo bài viết trước của tôi về delegates, nơi tôi giải thích cách chúng hoạt động và lý do tại sao chúng hữu ích.

Trong bài viết này, chúng ta sẽ tập trung vào built-in delegates được cung cấp bởi C#, chẳng hạn như Func, Predicate, Expression và Action. Các delegate này đều được sử dụng để đóng gói phương thức hoặc biểu thức, nhưng chúng có mục đích và ngữ cảnh sử dụng khác nhau.

1. Func

Func là một delegate đại diện cho một phương thức trả về một giá trị. Bạn có thể sử dụng nó khi cần một phương thức nhận một hoặc nhiều đầu vào và trả về một giá trị đầu ra. Nó có thể nhận tối đa 16 tham số và luôn trả về một giá trị của loại đã chỉ định.

Func có thể được sử dụng trong nhiều trường hợp khác nhau nhưng thường gặp nhất trong LINQ và các mẫu lập trình hàm trong .NET.

Cú pháp:

  • Func<TResult>: Một phương thức không có tham số và trả về TResult.
  • Func<T1, TResult>: Một phương thức nhận một tham số kiểu T1 và trả về TResult.

Ví dụ:

Chuyển đổi một số nguyên thành chuỗi:

Func<int, string> func = x => x.ToString();

string result = func(5); // “5”

Ở đây, Func nhận một số nguyên và trả về chuỗi tương ứng của nó.

2. Predicate

Predicate là một delegate chuyên biệt, đại diện cho một phương thức nhận một tham số và luôn trả về một giá trị kiểu bool, cho biết điều kiện có được thỏa mãn hay không. Do đó, ta có thể xem nó như một dạng đặc biệt của Func luôn trả về true hoặc false.

Predicate thường được sử dụng trong các phương thức kiểm tra điều kiện, chẳng hạn như lọc phần tử trong một danh sách.

Cú pháp:

  • Predicate<T>: Một phương thức nhận tham số kiểu T và trả về bool.

Ví dụ:

Kiểm tra xem một số có phải số chẵn hay không:

Predicate<int> isEven = x => x % 2 == 0;

bool isFiveEven = isEven(5); // false

Ở đây, Predicate kiểm tra xem số 5 có phải số chẵn hay không và trả về false.

3. Expression

Expression là một lớp được sử dụng để biểu diễn mã nguồn dưới dạng dữ liệu, đặc biệt là các biểu thức có thể biên dịch thành mã thực thi hoặc chuyển đổi thành các dạng khác như câu lệnh SQL trong LINQ-to-SQL.

Khác với Func, Expression không chỉ đơn thuần thực thi một phương thức, mà nó có thể được kiểm tra hoặc chuyển đổi trước khi chạy. Nó tạo ra một cây biểu thức (expression tree), có thể được phân tích hoặc sửa đổi trước khi thực thi.

Expression thường được sử dụng trong các trường hợp cần phân tích hoặc chuyển đổi biểu thức trước khi thực thi, chẳng hạn như trong các công cụ ORM hoặc LINQ providers.

Cú pháp:

  • Expression<Func<T, TResult>>: Đại diện cho một biểu thức lambda có thể được biên dịch hoặc diễn dịch.

Ví dụ:

Kiểm tra xem một số có lớn hơn 10 không:

Expression<Func<int, bool>> expr = x => x > 10;

// Bạn có thể biên dịch và chạy biểu thức này:

Func<int, bool> compiledExpr = expr.Compile();

bool isElevenGreaterThanTen = compiledExpr(11); // true

Ở đây, Expression tạo một cây biểu thức kiểm tra xem một số có lớn hơn 10 hay không, và ta có thể biên dịch nó thành một delegate để thực thi.

4. So sánh và cách áp dụng

Func vs Predicate

  • Dùng Func khi bạn cần một giá trị trả về bất kỳ.
  • Dùng Predicate khi bạn cần kiểm tra điều kiện và chỉ quan tâm đến kết quả true hoặc false.

Func/Predicate vs Expression

  • Dùng Func hoặc Predicate khi bạn muốn thực thi trực tiếp một delegate.
  • Dùng Expression khi bạn muốn phân tích, sửa đổi hoặc biên dịch logic trước khi thực thi. Expression đặc biệt hữu ích khi cần chuyển đổi mã nguồn, chẳng hạn như xây dựng truy vấn động hoặc chuyển đổi thành SQL trong LINQ.

5. Action

Ngoài Func, C# còn có một delegate tích hợp khác là Action.

Action giống với Func, nhưng không trả về giá trị nào. Nó được sử dụng khi bạn muốn truyền một phương thức thực hiện một hành động nhưng không cần trả về kết quả (tức là phương thức có kiểu trả về void).

Ví dụ:

// In ra màn hình: “Print action called!”

Action<string> print = message => Console.WriteLine(message);

print(“Print action called!”);

Ở đây, Action nhận một chuỗi làm đầu vào nhưng không trả về bất kỳ giá trị nào, mà chỉ thực hiện Console.WriteLine().

6. Tóm tắt

Mỗi loại delegate này có một mục đích cụ thể trong .NET:

  • Func: Đại diện cho một phương thức trả về một giá trị.
  • Predicate: Một dạng đặc biệt của Func luôn trả về true hoặc false.
  • Expression: Biểu diễn một đoạn mã dưới dạng cây biểu thức, có thể phân tích hoặc biên dịch.
  • Action: Giống Func nhưng không có giá trị trả về (trả về void).

Việc hiểu rõ sự khác nhau giữa các loại delegate này giúp bạn chọn loại phù hợp cho từng trường hợp cụ thể trong lập trình .NET.

#ntechdevelopers