Thursday, November 3, 2011

Delegate in C# Part3

Background

Đây là bài viết thứ 3 trong series 4 bài viết về delegate. Trong bài viết này, chúng ta sẽ tiếp tục thảo luận về delegate và giới thiệu một khái niệm mới – events. Bởi vì những bài viết trong series này mang tính chất liên tục với nhau. Vì vậy nếu chưa đọc qua 2 bài viết trước, bạn nên xem qua để hiểu rõ hơn. Sau đây là nội dung tóm tắt các bài trước.

Trong bài đầu tiên, tôi đã giới thiệu về delegate cũng như cách mà C# compiler làm việc với nó. Delegate là một đối tượng có kiểu riêng biệt. Nó được tạo ra bởi compiler, tham chiếu đến một hoặc nhiều function; khi phương thức Invoke của nó được gọi, tất cả các hàm mà nó tham chiếu ới sẽ được gọi lần lượt. Bài viết thứ 2 giải thích cách delegate sử dụng multithread như thế nào. Delegate có thể dùng phương thức Invoke để gọi đến các hàm được tham chiếu một cách đồng bộ hoặc sử dụng phương thức BeginInvoke/EndInvoke để tạo ra lời gọi không đồng bộ.
More about Delegates: A Different Approach

Sau 2 bài viết đầu tiên. Tôi đã nhận được một số phản hồi tích cực. Nhiều lập trình viên thích cách diễn đạt các khái niệm trong thế giới lập trình theo cách sử dụng những tình huống thực tế và sau đó chuyển nó thành các vấn đề trong lập trình. Khi tạo ra một mối liên hệ chặt chẽ giữa đời thực và programming, kĩ năng lập trình của bạn sẽ được cải thiện rất nhiều.
Bây giờ chúng ta hãy xem qua một vài ví dụ sử dụng delegate trong thực tế:
  1. Gọi thức ăn qua điện thoại: bạn sử dụng delegate để yêu cầu nhà hang chuẩn bị thức ăn cho mình
  2. Mua sắm trực tuyến: website chính là một delegate đại diện cho người bán hang
  3. Tham gia các khóa học trực tuyến: website là delegate đại diện cho trung tâm dạy học
  4. Nói chuyện với bạn bè qua email: email chính là delegate đại diện cho bạn của bạn
Bạn chưa từng nghĩ đến điều này đúng không? Nhưng đó là sự thật. Một delegate không chỉ là một người đại diện cho ai đó tại buổi hội thảo hay meeting mà còn là một thứ gì đó đại diện cho một thứ gì khác.
Các khái niệm về điện thoại, websites, etc… thực ra chính là những thể hiện của delegate trong thế giới lập trình. Vì vậy, khi bạn gọi đến một số điện thoại nào đó thì chính là khi bạn sử dụng delegate.
Và chúng ta có thể truyền delegate như một tham số. Nó cũng giống như bạn đưa số điện thoại của một nhà hàng cho bạn của bạn, và người bạn đó sử dụng lại số điện thoại để order.
Nhưng không chỉ có thế.

Với chiếc điện thoại, bạn không chỉ có thể order thức ăn, bạn còn có thể nói chuyện với mọi người, đặt vé máy bay, lên lịch hẹn…
Như vậy, một delegate – trong trường hợp này là điện thoại – có thể đại diện cho nhiều thứ khác nhau. Nó phụ thuộc vào số điện thoại và tin nhắn mà bạn gởi đi thông qua điện thoại đó.
Chúng ta không cần thiết phải có 4 cái điện thoại trong một căn hộ chỉ để gọi cho 4 công việc. Chỉ một cái là đủ.
Và delegate cũng vậy, chúng ta có thể sử dụng một delegate cho nhiều functions/methods khác nhau. Chỉ cần đảm bảo signature của các chúng phù hợp với signature của delegate.
Vậy làm sao chúng ta hiện thực những thứ trên trong thế giới lập trình?

Tưởng tượng hệ thống chúng ta có hàm thực hiện chức chức năng lưu lại các thông báo lỗi vào file. Nó trả về kiểu void và nhận tham số kiểu string. Một vài hàm khác tương tự nhưng thay vì lưu vào file nó sẽ lưu vào database, hoặc gởi tin nhắn cho người quản trị… tất cả đều return void và nhận 1 tham số kiểu string – error message.
Bạn có thể khai báo một public delegate đại diện cho các chức năng này. Nó phải có signature của hàm: return void và nhận một tham số kiểu string.
Trong chương trình của mình, bạn có thể tạo ra một thể hiện của delegate này và trỏ vào các hàm theo yêu cầu của công việc. Khi invoke delegate, các hàm sẽ lần lượt lưu tin nhắn lỗi vào từng nơi cụ thể tùy theo mỗi hàm.
Events

Bây giờ chúng ta sẽ tìm hiểu sâu hơn về events. Hầu hết các lập trình viên C# đều sử dụng event rất thường xuyên. Tôi thật sự không thể tưởng tượng ra được một sản phẩm phần mềm mà không sử dụng đến event. Điều này nghĩa là tất cả mọi người đều biết cách sử dụng event và việc này khiến cho tôi gặp một chút khó khan khi phải trình bày lại một thứ mà các bạn đã biết. Vì thế nên tôi sẽ cố gắng đưa ra một góc nhìn mới trong bài viết này.
Event là gì?
Theo từ điển của google, event is a thing that happens, esp one of importance (event – sự kiện – là một thứ gì đó sắp xảy ra, và mang tính chất quan trọng).
Nếu chúng ta muốn công bố một việc gì đó quan trọng, chúng ta có thể tổ chức một sự kiện cho việc đó. Và cũng như các bài viết trước, chúng ta sẽ dùng một ví dụ trong đời sống để làm ví dụ.
Ví dụ: Một gia đình đang chờ đợi sự ra đời của một đứa bé. Tuy nhiên, họ không thể biết chính xác ngày chào đời của nó được. Gia đình này muốn người than và bạn bè sẽ đến thăm người mẹ trong bệnh viện. Vậy nên họ thông báo sự kiện, công bố trên facebook và hỏi thăm những một vài người có thể đến tham gia sự kiện này.
Nếu tôi là một người bạn và tôi muốn đến tham gia bữa tiệc. Tôi có thể gọi cho họ, yêu cầu họ thông báo cho tôi biết khi nào thì đứa bé ra đời qua số điện thoại mà tôi cung cấp. Khi sự kiện diễn ra, họ sẽ gọi và thông báo tôi biết, sau đó tôi sẽ đến thăm gia đình này trong bệnh viện.
Chúng ta biết rằng khi gia đình đó thông báo sự kiện, họ đã sử dụng delegate. Nhưng nó vẫn chưa được thể hiện. Nếu nó được thể hiện thì đó là lúc mà tất cả những người tham gia sẽ nhận được lời mời. Đầu tiên, họ cần phải tạo ra một thể hiện của delegate này. Một hàm đặc biệt phải được tạo ra và được delegate tham chiếu đến. Khi sự kiện xảy ra, phương thức Invoke của event sẽ được gọi (nếu event không null tức là có người tham gia sự kiện). Lúc này tất cả những người đăng kí tham dự sẽ được thông báo bằng hàm đã tạo ra cho event.
Liên hệ giữa delegate và event

Event cũng là delegate. Mọi thứ mà chúng ta biết về delegate đều có thể áp dụng đối với event. Khi khai báo một event, bạn cần phải lựa chọn một delegate mà bạn vừa khai báo hoặc từ các delegate được khai báo sẵn trong .NET.
Khác nhau giữa event và delegate

Một vài điểm khác biệt giữa delegate và event:
  • Mặc dù event cũng là delegate, tuy nhiên ta không thể chỉ khai báo event thay cho delegate. Delegate phải luôn được khai báo trước event.
  • Khai báo event không yêu cầu phải xác định signature của function/method. Nó sử dụng lại signature được khai báo ở delegate.
  • Event thuộc chỉ có phạm vi bên trong class khai báo nó. Bạn không thể gọi đến event ở bên ngoài class (đối với delegate, bạn có thể invoke nó ở bất kì đâu).
  • Nếu bạn định nghĩa một interface, bạn không thể khai báo một delegate như là thành phần của interface đó, nhưng đối với event thì được.
  • Một vài yêu cầu đối với delegate nếu bạn muốn sử dụng nó như một event. Đó là signature của function return void, có 2 tham số là object sender và EventArgs e).
  • Không thế subscribe vào một delegate. Bạn cần phải khai báo event để có thể thực hiện thao tác đăng kí.
Code

MyFamilyEventHandler là một delegate sẽ được dùng để sau này khai báo event.
// declare delegate for future event public delegate void MyFamilyEventHandler(object sender, EventArgs e);

class Family khai báo một evetn là OnBabyBorn. Phương thức BabyBorn sẽ gọi event. Event này có kiểu MyFamilyEventHandler.
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
Bây giờ chúng ta tạo ra class Friend. Class này có một public property kiểu Family. Khi gán giá trị cho property này, đối tượng friend sẽ đăng kí tham gia sự kiện OnBabyBorn của đối tượng family. Cú pháp để đăng kí:
family.OnBabyBorn += new MyFamilyEventHandler(family_OnBabyBorn); Như chúng ta biết, khi compiler gặp đoạn code này, nó sẽ tạo ra một thể hiện của event (cũng là của delegate) và gán tham chiếu của function family_OnBabyBorn cho delegate. Từ bây giờ, khi gọi event (sử dụng phương thức Invoke), function này sẽ được gọi và thực thi.
Lưu ý, delegate được khai báo trong class Family. Vì vậy đối tượng friend phải lưu trữ đối tượng family, như vậy thì friend mới có thể đăng kí vào event.

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
///
/// Family friend
///
public class Friend
{
    private Family family;   public Family Family
    {
        get
        {
            return family;
        }
        set
        {
            family = value;   //subscribe for the family OnBabyBorn event:
            family.OnBabyBorn += new MyFamilyEventHandler
                        (family_OnBabyBorn);
        }
    }   public string Name { get; set; }   ///
    /// constructor
    ///
    ///
    public Friend(string name)
    {
        Name = name;
    }   ///
    /// run when OnBabyBorn event in
    /// family occurs
    ///
    ///
    ///
    void family_OnBabyBorn(object sender, EventArgs e)
    {
        Console.WriteLine("{0}, go visit {1} family", Name, sender.ToString() );
    }
}

Cuối cùng là hàm Main. Trong hàm này sẽ tạo ra một đối tượng Family và 2 đối tương Friend. Khi event được gọi, cả 2 friend đều nhận được thông báo.
1
2
3
4
5
6
7
8
9
10
11
12
13
class Program
{
    static void Main(string[] args)
    {
        //create new family:
        Family family = new Family("Adams");   //create a friend:
        Friend Ed = new Friend("Ed");   //assign the family to the friend:
        Ed.Family = family;   //create another friend:
        Friend Alex = new Friend("Alex");   //assign the family to the friend:
        Alex.Family = family;   //baby is born
        family.BabyBorn();   Console.Read();
    }
}

Còn đây là kết quả:

Hi vọng bài viết này sẽ giúp bạn hiểu rõ hơn về delegate và event.
To be continued…
Source: Delegate in C# – Attempt to look inside – Part 3
///
/// Family that waits for a baby
///
public class Family
{
    ///
    /// declare event of MyFamilyEventHandler
    /// type
    ///
    public event MyFamilyEventHandler OnBabyBorn;   public string Name { get; set; }   ///
    /// constructor
    ///
    ///
    public Family(string name)
    {
        Name = name;
    }   ///
    /// raise OnBabyBorn event;
    ///
    public void BabyBorn()
    {
        if (null != OnBabyBorn)
            OnBabyBorn(Name, new EventArgs());
    }
}

1 comment:

  1. I just couldn't go away your web site before suggesting that I actually loved the usual information an individual supply to your guests? Is going to be back steadily to check up on new posts

    Look into my page anti cellulite treatment

    ReplyDelete