Bài viết thứ 4, cũng là bài viết cuối cùng trong series bài viết về delegate này sẽ tiếp tục đào sâu hơn về delegate trong .NET.
More Delegates in .NET
Sau mỗi phiên bản .NET Framework ra đời, tính ứng dụng của delegates càng được mở rộng, cung cấp cho người dùng những giải pháp giúp đơn giản hóa công việc hơn. Bảng dưới đây sẽ liệt kê tên gọi và mô tả về các loại delegate được cung cấp trong phiên bản .NET 3.5 (trong .NET 4.0 còn có nhiều hơn thế này). Để có thể sử dụng delegate, trước hết chúng ta cần phải khai báo chúng. Trong quá trình khai báo, chúng ta quy định signature của function sẽ được đại diện bởi delegate mà chúng ta đang khởi tạo. Nhưng nhờ có những delegate được xây dựng sẵn, chúng ta có thể bỏ qua phần khai báo. Không chỉ cung cấp cho lập trình viên tập hợp delegates rất tiện ích này, .NET còn cung cấp các kiểu Generic delegate (delegate hoạt động với tham số có kiểu dữ liệu bất kì do người dùng chỉ định).
Delegate
Description
Action
Chứa một phương thức không có kiểu trả về và không có tham số.
Action
Tương tự như Action nhưng được phép nhận một tham số
Action>
Nhận được 2 tham số.
Action
Nhận được 3 tham số.
Action
Nhận được 4 tham số.
AppDomainInitializer
Đại diện cho hàm callback, hàm này sẽ được gọi khi application domain được khởi tạo.
AssemblyLoadEventHandler
Represents the method that handles the AssemblyLoad event of an AppDomain.
Đại diện cho phương thức xử lý sự kiện AssemblyLoad của một AppDomain
AsyncCallback
Tham chiếu đến một phương thức sẽ được gọi khi một hoạt động không đồng thời nào đó hoàn tất.
Comparison
Đại diện cho phương thức dùng để so sánh 2 đối tượng cùng kiểu dữ liệu.
ConsoleCancelEventHandler
Đại diện cho phương thức sẽ xử lý sự kiện CancelKeyPress của System.Console.
Converter
Đại diện cho một phương thức dùng để chuyển đổi kiểu của một đối tượng sang kiểu khác.
CrossAppDomainDelegate
Used by DoCallBack for cross-application domain calls.
Sử dụng DoCallBack trong các lời gọi tới các miền thuộc ứng dụng khác
EventHandler
Đại diện cho phương thức sẽ xử lý một sự kiện không có dữ liệu về sự kiện đó.
EventHandler
Đại diện cho một phương thức sẽ xử lý một sự kiện
Func
Tham chiếu đến một phương thức có kiểu trả về là kiểu được quy định bởi tham số TResult, ngoài ra không nhận thêm tham số nào khác.
Func
Tương tự như Funcnhưng được phép nhận một tham số.
Func
Có thể nhận được 2 tham số.
Func
Có thể nhận được 3 tham số.
Func
Có thể nhận được 4 tham số.
Predicate
Đại diện cho một phương thức sẽ quy định một tập các yêu cầu, chuẩn mực và xác định liệu một đối tượng nhất định có đạt được yêu cầu này hay không.
ResolveEventHandler
Đại diện cho phương thức xử lý các sự kiện của một AppDomain: TypeResolve, ResourceResolve, và AssemblyResolve
UnhandledExceptionEventHandler
Đại diện cho phương thức sẽ xử lý sự kiện gây ra bởi một ngoại lệ không xác định.
Goal
Trong bài viết này, tôi muốn trình bày những kĩ thuật khác nhau khi sử dụng custom delegates hoặc pre-define delegates. Chúng ta có thể sắp xếp các loại delegates được liệt kê ở bảng trên ra thành các loại sau đây:
1. Generic type function and methods.
2. Event handler – xử lý sự kiện
- Action
- Func
- Converter
- Comparision
- Predicate
3. Others – các loại khác
- AssemblyLoadEventHandler
- ConsoleCancelEventHandler
- EventHandler
- EventHandler
- ResolveEventHandler
- UnhandledExceptionEventHandler
Trong bài viết này, tôi sẽ chỉ nói về 3 delegate đầu tiên là Action, Func, Converter. Tôi muốn mô tả cách sử dụng những delegate này bằng nhiều cách khác nhau. Mặc dù compiler sẽ sinh ra những đoạn code tương tự nhau, nhưng đứng từ góc độ lập trình, các kĩ thuật này khác nhau rất nhiều.
- AppDomainInitializer
- AsyncCallback (chúng ta đã từng làm quen với delegate này trong phần 2)
- CrossAppDomainDelegate
Action và Func
Action có thể đại diên cho bất kì phương thức nào return void và có thể nhận tối đa 4 tham số (trong .NET 4.0 là 8). Action: T1 và T2 là các tham số và chúng có kiểu dữ liệu bất kì. Func cũng giống như Action, chỉ khác ở chỗ Func trả về giá trị của một kiểu bất kì. Func : T1, T2 là các tham số có kiểu bất kì và TResult là kiểu trả của giá trị trả về. Như vậy, sẽ không có vấn đề gì nếu tôi chỉ giới thiệu về Action vì Func cũng tương tự như vậy.
Ví dụ sử dụng delegate thông thường
Bên trong namespace của project, tôi khai báo delegate của mình:
public delegate void ShootingHandler(int times);
delegate này return void và nhận một tham số kiểu int. Và bên trong class dưới đây sử dụng biến “shoot” có kiểu của delegate này.
Còn đây là class:
1234567891011121314151617181920212223242526272829303132333435363738394041424344public
class
GunClassicDelegate
{
private
ShootingHandler shoot;
public
string
Name {
get
;
set
; }
public
GunClassicDelegate(
string
name)
{
Name = name;
switch
(Name.ToLower())
{
case
"automatic gun"
:
shoot =
new
ShootingHandler(ShootAutomatic);
break
;
case
"paint gun"
:
shoot =
new
ShootingHandler(ShootPaint);
break
;
default
:
shoot =
new
ShootingHandler(ShootSingle);
break
;
}
}
public
void
Fire(
int
times)
{
shoot(times);
}
private
void
ShootAutomatic(
int
times)
{
Console.WriteLine(
"Automatic shooting: "
);
for
(
int
i = 0; i < times; i++)
Console.Write(
"Biff..."
);
Console.WriteLine();
}
private
void
ShootSingle(
int
times)
{
Console.WriteLine(
"Single action shooting: "
);
for
(
int
i = 0; i < times; i++)
Console.WriteLine(
"Bang"
);
}
private
void
ShootPaint(
int
times)
{
Console.WriteLine(
"Splashing paint "
);
for
(
int
i = 0; i < times; i++)
Console.WriteLine(
"Bloop"
);
}
}
Trong constructor, tùy thuộc vào tên được truyền vào, tôi sẽ truyền vào thể hiện của delegate một trong các hàm sau:
Như các bạn thấy, tất cả đều diễn ra hợp lệ bởi các hàm này đều có cùng signature với delegate. Tôi sử dụng một hàm public Fire, trong hàm này delegate sẽ được gọi.
- ShootAutomatic
- ShootSingle
- ShootPaint
Sử dụng Action delegate do .NET xây dựng
1234567891011121314151617181920212223242526272829303132333435363738394041424344public
class
GunGenericDelegate
{
private
Action<
int
> shoot;
public
string
Name {
get
;
set
; }
public
GunGenericDelegate(
string
name)
{
Name = name;
switch
(Name.ToLower())
{
case
"automatic gun"
:
shoot = ShootAutomatic;
break
;
case
"paint gun"
:
shoot = ShootPaint;
break
;
default
:
shoot = ShootSingle;
break
;
}
}
public
void
Fire(
int
times)
{
shoot(times);
}
private
void
ShootAutomatic(
int
times)
{
Console.WriteLine(
"Automatic shooting: "
);
for
(
int
i = 0; i < times; i++)
Console.Write(
"Biff..."
);
Console.WriteLine();
}
private
void
ShootSingle(
int
times)
{
Console.WriteLine(
"Single action shooting: "
);
for
(
int
i = 0; i < times; i++)
Console.WriteLine(
"Bang"
);
}
private
void
ShootPaint(
int
times)
{
Console.WriteLine(
"Splashing paint "
);
for
(
int
i = 0; i < times; i++)
Console.WriteLine(
"Bloop"
);
}
}
public Action<int> shoot; Việc này báo cho compiler biết rằng biến shoot là một thể hiện của một delegate kiểu void và nhận một tham số kiểu int. Constructor trở nên đơn giản hơn, ta chỉ việc gán function cho biến shoot:
shoot = ShootSingle; Sử dụng Action với từ khóa delegate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class GunGenericInLineDelegate { public string Name { get ; set ; } private Action< int > shoot; public void Fire( int times) { shoot(times); } public GunGenericInLineDelegate( string name) { Name = name; switch (Name.ToLower()) { case "automatic gun" : shoot = delegate ( int times) { Console.WriteLine( "Automatic shooting: " ); for ( int i = 0; i < times; i++) Console.Write( "Biff..." ); Console.WriteLine(); }; break ; case "paint gun" : shoot = delegate ( int times) { Console.WriteLine( "Splashing paint " ); for ( int i = 0; i < times; i++) Console.WriteLine( "Bloop" ); }; break ; default : shoot = delegate ( int times) { Console.WriteLine( "Single action shooting: " ); for ( int i = 0; i < times; i++) Console.WriteLine( "Bang" ); }; break ; } } } |
Trong ví dụ trên, tôi bỏ qua việc khai báo 3 private methods. Nó giúp cho chương trình của tôi ngắn hơn nhiều. Và thay vào đó, chức năng của các function bị lượt bỏ đi sẽ được khai báo ngay trong constructor. Cú pháp của kĩ thuật này rất đơn giản:
shoot = delegate(int times) { //the functionality goes here }; Compiler biết rằng “shoot” có kiểu delegate. Vì vậy nên nó cho phép bạn sử dụng từ khóa “delegate” và sử dụng nó để khai báo inline function.
Sử dụng Action delegate và LAMBDA expression
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class GunGenericInLineLambda { public string Name { get ; set ; } public Action< int > shoot; public void Fire( int times) { shoot(times); } public GunGenericInLineLambda( string name) { Name = name; switch (Name.ToLower()) { case "automatic gun" : shoot = (times) => { Console.WriteLine( "Automatic shooting: " ); for ( int i = 0; i < times; i++) Console.Write( "Biff..." ); Console.WriteLine(); }; break ; case "paint gun" : shoot = (times) => { Console.WriteLine( "Splashing paint " ); for ( int i = 0; i < times; i++) Console.WriteLine( "Bloop" ); }; break ; default : shoot = (times) => { Console.WriteLine( "Single action shooting: " ); for ( int i = 0; i < times; i++) Console.WriteLine( "Bang" ); }; break ; } } } |
Lambda expressions đã được giới thiệu từ nhiều năm trước, và đến thời điểm này nó đang rất được các lập trình viên C# ưa dùng. Cách sử dụng của nó cũng tương tự như ví dụ trên. Thay vì sử dụng delegate function, tôi sử dụng Lamda expression. Các bạn có thể tìm hiểu thêm về Lambda expression từ rất nhiều các tài liệu trên mạng. Cú pháp của sử dụng lambda expression như sau:
(i) => { //functionality goes here} Trong đó, (i) là danh sách các tham số mà chúng ta cần sử dụng bên trong hàm. Nếu delegate function này không yêu cầu tham số, ta chỉ cần khai báo (). => {…….} .
Bên trong cặp dấu ngoặc nhọn, bạn sẽ cần phải khai báo các chức năng cho delegate function, và các chức năng này sử dụng tham số mà bạn truyền vào. Một lần nữa, bởi vì giữa việc sử dụng Action và Func chỉ khác nhau ở giá trị trả về, do đó tôi sẽ không đưa ví dụ về việc sử dụng Func.
Dưới đây là đoạn chương trình chính:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public class ActionExample { static void Main( string [] args) { //part - classic: Console.WriteLine( "Shooting classic delegate:" ); GunClassicDelegate gunClassic1 = new GunClassicDelegate( "automatic gun" ); GunClassicDelegate gunClassic2 = new GunClassicDelegate( "paint gun" ); GunClassicDelegate gunClassic3 = new GunClassicDelegate( "hand gun" ); gunClassic1.Fire(4); gunClassic2.Fire(4); gunClassic3.Fire(4); //part - generic delegate: Console.WriteLine( "Shooting generic delegate:" ); GunGenericDelegate gunGeneric1 = new GunGenericDelegate( "automatic gun" ); GunGenericDelegate gunGeneric2 = new GunGenericDelegate( "paint gun" ); GunGenericDelegate gunGeneric3 = new GunGenericDelegate( "hand gun" ); gunGeneric1.Fire(4); gunGeneric2.Fire(4); gunGeneric3.Fire(4); //part - generic inline delegate: Console.WriteLine( "Shooting generic inline delegate:" ); GunGenericInLineDelegate gunGenericInLine1 = new GunGenericInLineDelegate( "automatic gun" ); GunGenericInLineDelegate gunGenericInLine2 = new GunGenericInLineDelegate( "paint gun" ); GunGenericInLineDelegate gunGenericInLine3 = new GunGenericInLineDelegate( "hand gun" ); gunGenericInLine1.Fire(4); gunGenericInLine2.Fire(4); gunGenericInLine3.Fire(4); //part - generic lambda delegate: Console.WriteLine( "Shooting generic lambda delegate:" ); GunGenericInLineLambda gunGenericInLineLambda1 = new GunGenericInLineLambda( "automatic gun" ); GunGenericInLineLambda gunGenericInLineLambda2 = new GunGenericInLineLambda( "paint gun" ); GunGenericInLineLambda gunGenericInLineLambda3 = new GunGenericInLineLambda( "hand gun" ); gunGenericInLineLambda1.Fire(4); gunGenericInLineLambda2.Fire(4); gunGenericInLineLambda3.Fire(4); Console.Read(); } } |
Khi chạy chương trình bạn sẽ thấy không hề có sự khác biệt trong kết quả xuất ra màn hình ở mỗi ví dụ.
Converter
Delegate Converter<TInput,TOutput> là một tham số trong phương thức Array.ConvertAll
Dưới đây là những class mà chúng ta cần:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Person { public string FirstName { get ; set ; } public string LastName { get ; set ; } public Person( string firstname, string lastname) { FirstName = firstname; LastName = lastname; } } public class PersonInfo { public string FirstName { get ; set ; } public string LastName { get ; set ; } public string FullName { get { return FirstName + " " + LastName; } } public static PersonInfo ConvertToPersonInfo(Person person) { PersonInfo res = new PersonInfo(); res.FirstName = person.FirstName; res.LastName = person.LastName; return res; } } |
Ở đây có 2 class, Person và PersonInfo. Chỉ có một điểm khác biệt nhỏ giữa 2 class này, tuy nhiên đối với compiler thì nó hoàn toàn khác nhau. Nếu tôi muốn chuyển đổi một mảng Person thành mảng chứa các đối tượng kiểu PersonInfo, tôi phải sử dụng delegate Converter. Do đó, tôi đã tạo ra một static function ConvertToPersonInfo, hàm này sẽ nhận tham số kiểu Person và return đối tượng kiểu PersonInfo. Hàm này thuộc về class PersonInfo. Trong đoạn code ví dụ dưới đây, tôi sẽ trình bày 3 hàm Main khác nhau: MainClassic, MainLambda, MainDelegate. Mỗi hàm Main có cách sử dụng delegate khác nhau. Bạn cần phải chọn một trong số 3 hàm Main này để biên dịch chương trình.
Trong hàm MainClassic sẽ tham chiếu đến phương thức ConvertToPersonInfo. Còn 2 hàm Main còn lại sử dụng từ khóa delegate và lambda expression thay vì tham chiếu đến một hàm đã tạo ra sẵn.
Conclusions
Còn có rất nhiều chuyện để nói về delegate trong C#, nhưng tôi sẽ kết thúc series bài viết này tại đây. Tôi chỉ muốn mang lại cho các bạn một cách nhìn đơn giản về delegate. Như các bạn đã thấy, bạn có thể lựa chọn rất nhiều cách khác nhau để sử dụng delegate trong chương trình của mình. Nếu không có delegate được xây dựng sẵn, bạn có thể tạo ra một cái để sử dụng cho mình. Ngoài ra, bạn có thể tận dụng những tiện lợi của các hàm inline thay vì phải tạo ra một hàm, đặt tên cho nó và gán nó cho delegates. Bạn cũng có thể sử dụng của lambda expression. Một lần nữa, đây là bài viết cuối cùng trong series bài viết của tôi về delegate, rất cảm ơn vì các bạn đã ủng hộ.
Bài viết nguyên mẫu: Delegate in C# – Part 4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Program { static void MainClassic( string [] args) { Person[] people = { new Person( "ed" , "g" ), new Person( "al" , "g" ) }; PersonInfo[] newpeople = Array.ConvertAll(people, new Converter } static void MainLambda( string [] args) { Person[] people = { new Person( "ed" , "g" ), new Person( "al" , "g" ) }; PersonInfo[] newpeople = Array.ConvertAll(people, (p) => { PersonInfo pi = new PersonInfo(); pi.LastName = p.LastName; pi.FirstName = p.FirstName; return pi; }); } static void MainDelegate( string [] args) { Person[] people = { new Person( "ed" , "g" ), new Person( "al" , "g" ) }; PersonInfo[] newpeople = Array.ConvertAll(people, delegate (Person p) { PersonInfo pi = new PersonInfo(); pi.LastName = p.LastName; pi.FirstName = p.FirstName; return pi; }); } } |
Conclusions
Còn có rất nhiều chuyện để nói về delegate trong C#, nhưng tôi sẽ kết thúc series bài viết này tại đây. Tôi chỉ muốn mang lại cho các bạn một cách nhìn đơn giản về delegate. Như các bạn đã thấy, bạn có thể lựa chọn rất nhiều cách khác nhau để sử dụng delegate trong chương trình của mình. Nếu không có delegate được xây dựng sẵn, bạn có thể tạo ra một cái để sử dụng cho mình. Ngoài ra, bạn có thể tận dụng những tiện lợi của các hàm inline thay vì phải tạo ra một hàm, đặt tên cho nó và gán nó cho delegates. Bạn cũng có thể sử dụng của lambda expression. Một lần nữa, đây là bài viết cuối cùng trong series bài viết của tôi về delegate, rất cảm ơn vì các bạn đã ủng hộ.
Bài viết nguyên mẫu: Delegate in C# – Part 4.
whoah this blog is great i like reading your articles. Keep up the
ReplyDeletegood work! You recognize, many people are searching around for this information, you
could aid them greatly.
Also visit my website: cellulite treatment reviews