C# – Tìm hiểu về Anonymous method

Anonymous method (tạm dịch là phương thức vô danh) là phương thức không có tên được khai báo với từ khóa delegate. Anonymous method cho phép bạn tạo ra các hành động cho một cho delegate với cách viết inline.

1. Giới thiệu

Giả sử bạn có một phương thức AddListItem() dùng để thêm một ListViewItem vào ListView. Bạn khai báo một delegete và tạo một thể hiện của nó để sử dụng:

delegate void AddListItemHandler(ListViewItem item);

private void AddListItem(ListViewItem item)

{

listView1.Items.Add(item);

}

…

AddListItemHandler addItemHandler = new AddListItemHandler(AddListItem);

Việc khai báo phương thức AddListItem nếu chỉ để sử dụng một lần cho delegate này thì có vẻ như không cần thiết. Thay vì viết như trên ta có thể sử dụng anonymous method để rút ngắn lại mã lệnh cần viết và hạn chế số lượng phương thức không cần thiết:

delegate void AddListItemHandler(ListViewItem item);

AddListItemHandler addItemHandler = delegate(ListViewItem item)

{

listView1.Items.Add(item);

};

Trong trường hợp viết mã lệnh xử lý cho các sự kiện của một đối tượng nào đó, bạn có thể sử dụng phương pháp này nếu như thấy thích hợp, chú ý các tham số phải khớp với delegate được khai báo:

  • Predicate<T>
button1.Click += delegate(object sender, EventArgs e)

    {

    MessageBox.Show(“Bạn vừa click button1″);

    };

2. Sử dụng Predicate, Action và Comparison
Đây là ba delegate được xây dựng sẵn và dùng khá nhiều trong C# 2. Chúng được khai báo như sau:

  
  public delegate bool Predicate (T obj)

    public delegate void Action (T obj)

    public delegate int Comparison (T x, T y)

Predicate được sử dụng để kiểm tra các giá trị có thỏa mãn một điều kiện nào đó không, như bạn cũng có thể nó trả về kiểu bool.

Action sử dụng để thực hiện các hành động với đối tượng mà bạn truyền vào, và không trả về giá trị nào cả.

Comparisondùng để so sánh hai đối tượng cùng kiểu, thường sử dụng trong các trường hợp sắp xếp.

  • Predicate<T>

Để thấy được công dụng của nó, chúng ta thử đi khảo sát lớp Array với các phương thức tĩnh của nó. Bạn có thể thấy là lớp Array này cung cấp khá nhiều phương thức generic. Ta lấy ví dụ về phương thức thường được sử dụng là FindAll dùng để tìm tất cả các phần tử thỏa mãn điều kiện, nó được định nghĩa như sau:

    T[] Array.FindAll(T[] array, Predicate match)

Một phương thức generic có thể làm việc với bất kì kiểu nào, điều này rõ ràng là cần thiết vì nó làm việc với bất kì mảng thuộc kiểu dữ liệu nào mà bạn truyền vào. Ở đây ta quan tâm tới tham số thứ hai Predicatematch là một delegate xác định điều kiện cho phần tử cần tìm kiếm. Ví dụ ta có một mảng int[]:

int[] array ={ 10, 4, 3, 2, 8, 6, 5, 7, 9, 1 };

Nhiệm vụ của ta là lấy ra tất cả phần tử chẵn trong mảng. Bằng cách sử dụng anonymous method ta có thể viết như sau:

    int[] evens = Array.FindAll(array, delegate(int n)

    {

    return n % 2 == 0;

    });

Theo cách thông thường bạn không cần viếtvào sau tên phương thức, trình biên dịch sẽ tự động xác định kiểu của tham số.

   
 int[] evens = Array.FindAll(array, delegate(int n)

    {

    return n % 2 == 0;

    });

Action<T>
Bây giờ ta đã có một mảng evens chứa các số chẵn trong mảng array. Công việc tiếp theo là in chúng ra màn hình để xem kết quả. Bạn nghĩ tới việc dùng cấu trúc lặp foreach để làm việc này. Rất tốt nhưng vì đang bàn về chủ đề này nên ta sẽ tìm kiếm một cách thức khác tiện lợi hơn. Thật may mắn là nó nằm sẵn trong lớp Array với tên phương thức là ForEach (bạn cũng có thể tìm thấy phương thức này trong các kiểu collection khác).

Cú pháp của phương thức:

void Array.ForEach(T[] array, Action action)

Ở đây nếu bạn đã hiểu được các vấn đề được nói tới ở trên thì công việc lúc này trở nên rất đơn giản. Bạn có thể tự viết được mã lệnh để sử dụng phương thức ForEach này với anonymous method. Nếu chưa bạn cũng có thể xem cách làm tương tự như dưới đây:

Array.ForEach(evens, delegate(int n) { Console.WriteLine(n); });

Như bạn có thể thấy trong phần định nghĩa Predicatevà Action, chúng yêu cầu duy nhất một tham số với kiểu bất kì. Ở trên vì đang làm việc với kiểu int nên ta sẽ truyền vào delegate kiểu int và tên biến tự đặt là n. Sau đó chỉ việc dùng tham số n này trong khối lệnh của anonymous method.

  • Comparison<T>

Một ngày đẹp trời bạn coi lại ví dụ này và thấy các phần tử của mảng sắp xếp quá lộn xộn, và bạn muốn sắp xếp lại chúng nhưng lại không muốn sử dụng các thuật toán sắp xếp. Bạn tìm và thấy phương thức Sort trong lớp Array và bắt đầu thực hiện, kết quả thật mĩ mãn. Bạn chú ý đến overload thứ 5 của phương thức Sort này, nó được định nghĩa:

void Array.Sort (T[] array, Comparison comparison)

Bạn thấy thích thú và muốn sử dụng nó, chú ý rằng nó yêu cầu hai tham số.

Array.Sort(evens, delegate(int x, int y)

{

return x.CompareTo(y);

});

Rất đơn giản nhưng bạn tự hỏi tại sao phải sử dụng cách này trong khi các overload khác của nó dễ sử dụng hơn nhiều? Đặt trường hợp bạn có một danh sách các đối tượng kiểu NhanVien có nhiều thuộc tính, và trong nhiều ngữ cảnh khác nhau, bạn cần sắp xếp danh sách này theo một thuộc tính riêng (chẳng hạn xếp theo tên, tuổi, lương,…) thì rõ ràng cách dùng Comparisonlà linh hoạt và dễ dàng nhất.

3. Phần kết

Bạn đã làm quen và nắm được cơ bản cách sử dụng anonymous method, đây là một bước cần thiết để giúp bạn tiếp thu một kiến thức khác mà tôi sẽ giới thiệu trong các bài sau, đó là về Lambda Expression. Hy vọng bạn có thể áp dụng các kiến thức trong bài này trong việc giải quyết các vấn đề và phát triển ứng dụng của mình.