- Giới thiệu delegate
- Sử dụng delegate cơ bản
- Chuỗi hàm trong delegate
- Func, Action delegate
- Sử dụng Delegate là tham số phương thức
Giới thiệu delegate
Delegate (hàm ủy quyền) là một kiểu dữ liệu,
nó dùng để tham chiếu (trỏ đến) đến các hàm (phương thức) có tham số và kiểu trả về phù hợp với khai báo kiểu.
Khi dùng đến delegate
bạn có thể gán vào nó một, nhiều hàm (phương thức) có sự tương thích về tham số,
kiểu trả về, sau đó dùng nó để gọi hàm (giống con trỏ trong C++), các event
trong C#
chính là các hàm được gọi thông qua delegate
,
bạn cũng có thể dùng delegate để xây dựng các hàm callback,
đặc biệt là các Event
Delegate
được dùng phổ biến khi gán các biểu thức lambda
Ví dụ sử dụng delegate
1 Đầu tiên cần khai báo một delegate,
khai báo giống như cách khai báo phương thức nhưng có thêm từ khóa delegate
và không có thân
phương thức.
Ví dụ sau khai báo một delegate có tên là ShowLog
public delegate void ShowLog(string message);
2 Khi đã có ShowLog,
nó dùng như một kiểu dữ liệu để khai báo các biến, các biến này có thể gán vào nó các hàm có
sự tương đồng về tham số và kiểu trả về với khai báo delegate
ví dụ khai báo biến:
ShowLog showLog;
Thi hành delegate
Sau khi biến delegate được gán hàm vào, có thể dùng biến
delegate để thi hành bằng cách gọi:
varDelegate.Invoke(các-tham-số) hoặc
varDelegate(các-tham-số)
3
Tạo hai phương thức Info
và Warning
có tham số giống với ShowLog, nghĩa
là có một tham số kiểu string, trả về void:
static public void Info(string s) { // ... } static public void Warning(string s) { // ... }
Do Info
, Warning
có tương đồng về tham số với delegate trên, nên hai hàm
này có thể dùng để gán vào biến kiểu ShowLog
, xem đoạn mã đầy đủ sau:
Logs.cs
using System; namespace CS008_Anonymous { public class Logs { // Khai báo một delegate public delegate void ShowLog(string message); // Phương thức tương đồng với ShowLog (tham số, kiểu trả về) static public void Info(string s) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(string.Format("Info: {0}", s)); Console.ResetColor(); } // Phương thức tương đồng với ShowLog (tham số, kiểu trả về) static public void Warning(string s) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(string.Format("Waring: {0}", s)); Console.ResetColor(); } public static void TestShowLog() { ShowLog showLog; showLog = Info; // showLog gán bằng phương thức Info showLog("Thông báo"); // Thi hành delegate chính là thi hành Info showLog = Warning; // showLog gán bằng phương thức Warning showLog("Thông báo"); // Thi hành delegate chính là thi hành Info } } }
Kết quả chạy đoạn, khi gọi hàm Logs.TestShowLog();
:
Waring: Thông báo Info: Thông báo
Gán nhiều phương thức vào delegate
Khi dùng delegate chạy một phương thức,
cần đảm bảo biến delegate đó đã được gán phương thức (biến khác null),
có thể bạn kiểm tra trước khi gọi ví dụ:
if (showLog != null) showLog("Mgs")
hoặc gắn gọn hơn
showLog?.Invoke("Mgs");
4 Một delegate có thể đưa vào nó nhiều phương thức để một lần gọi thi hành tất cả các phương thức nó chứa
- Toán tử
+=
Nối thêm một phương thức vào delegate, ví dụdelegatevar += method1
- Toán tử
-=
: Loại bỏ 1 phương ở cuối (nếu phương thức đó có trong delegate, tính từ cuối) , ví dụdelegatevar -= method1
Ví dụ:
public static void TestShowLogMulti() { ShowLog showLog; showLog = null; showLog += Warning; // Nối thêm Warning vào delegate showLog += Info; // Nối thêm Info vào delegate showLog += Warning; // Nối thêm Warning vào delegate //Một lần gọi thi hành tất cả các phương thức trong chuỗi delegate showLog("TestLog"); //Hoặc an toàn: showLog?.Invoke("TestLog"); }
Gọi phương thức TestShowLogMulti
thì kết quả:
Waring: TestLog Info: TestLog Waring: TestLog
5 Ngoài cách gán cho delegate một hàm có tên cụ thể như trên, bạn cũng có thể gán một phương thức Anonymou, ví dụ:
showLog += (x) => Console.WriteLine(string.Format("===>{0}<===", x));
6 Các delegate cùng kiểu có thể kết hợp lại với nhau bằng toán tử +
, ví dụ:
// Cộng nhiều Delegate public static void TestShowLogPlus() { ShowLog showLog1 = (x)=> {Console.WriteLine($"-----{x}-----");}; ShowLog showLog2 = Warning; ShowLog showLog3 = Info; var all = showLog1 + showLog2 + showLog3 + showLog1; all("Xin Chào"); }
Gọi phương thức TestShowLogPlus
kết quả là:
-----Xin Chào----- Waring: Xin Chào Info: Xin Chào -----Xin Chào-----
Func và Action
Func
và Action
là hai mẫu delegate định nghĩa sẵn, giúp bạn nhanh chóng tạo ra
biến kiểu delegate mà không mất công khai báo, xem lại ví dụ trên nếu sử dụng đến Func
,
Action
thì không cần có dòng khai báo:
public delegate void ShowLog(string message);
Sử dụng Func
Func là mẫu delegate
có kiểu trả về. Để khai báo biến delegate dùng cú pháp như
sau:
Func<kiểu_tham_số_1, kiểu_tham_số_2, ..., kiểu_trả_về> var_delegate;
Kiểu cuối cùng trong khai báo Func là kiểu trả về của hàm, có thể thiếu tham số nhưng không được thiếu kiểu trả về
Ví dụ muốn có biến delegate
tên bien1
tương đương với hàm có 2 tham số, tham số 1 kiểu int, tham số 2
kiểu string, và hàm trả về kiểu bool thì tạo biến đó như sau:
Func<int, string, bool> bien1;
Khai báo trên nếu bạn dùng cách thông thường tương ứng với:
// Khai báo delegate ở lớp delegate bool DelegateName(int a, string b); // Khai báo biến trong phương thức DelegateName bien1;
Ví dụ:
using System; namespace CS008_Anonymous { class FuncAction { static int Sum(int x, int y) { return x + y; } public static void TestFunc(int x, int y) { Func<int,int,int> tinhtong; // biến tinhtong phù hợp với các hàm trả về kiểu int, có 2 tham số kiểu int tinhtong = Sum; // Hàm Sum phù hợp nên có thể gán cho biến var ketqua = tinhtong(x, y); Console.WriteLine(ketqua); } } }
Khi gọi phương thức TestFunc
kết quả:
FuncAction.TestFunc(5, 6); // In ra: 11
Sử dụng Action
Action
tương tự như Func
, điều khác duy nhất là nó không có kiểu trả về,
khai báo cơ bản như sau:
Action<kiểu_tham_số_1, kiểu_tham_số_2, ... > var_delegate;
Nghĩa là biến kiểu Action có thể gán bằng các hàm có kiểu trả về void
Trở lại ví dụ cho hai hàm Info
và Warning
ở trên, có thể sử
dụng ngay đoạn code sau,
để có kết quả tương tự:
public static void TestAction(string s) { Action<string> showLog = null; showLog += Logs.Warning; // Nối thêm Warning vào delegate showLog += Logs.Info; // Nối thêm Info vào delegate showLog += Logs.Warning; // Nối thêm Warning vào delegate // Một lần gọi thi hành tất cả các phương thức trong chuỗi delegate showLog("TestLog"); }
Sử dụng Delegate làm tham số hàm
Có thể sử dụng delegate làm tham số của phương thức, nó có vai trò như những hàm callback linh hoạt. Xem ví dụ sau:
// Sử dụng Delegate làm tham số phương thức, truyền callback static void TinhTong(int a, int b, Actioncallback) { int c = a + b; // Gọi callback callback(c.ToString()); } public static void TestTinhTong() { TinhTong(5,6, (x) => Console.WriteLine($"Tổng hai số là: {x}")); TinhTong(1,3, Logs.Info); }
Source code: CS008_Anonymous (Git), hoặc tải ex008-1