C# Advanced (Series #9)

6 min read

c#
  • C# – Anonymous Types
  • C# – Dynamic Types
  • C# – Ternary Operator ?:
  • C# Generics & Generic Constraints
  • C# – Delegates
  • C# – Func Delegate
  • C# – Action Delegate
  • C# – Anonymous Method
  • C# – Events
  • C# – Extension Method
  • C# – HttpClient

C# – Events

Một event là thông báo được gửi bởi một đối tượng để báo hiệu sự xuất hiện của một hành động. Các event trong .NET tuân theo observer design pattern.

class raise events được gọi là Publisher và class nhận thông báo được gọi là Subscriber. Có thể có nmultiple subscribers của một event. Thông thường, publisher đưa ra một sự kiện khi một số hành động xảy ra. Những subscribers quan tâm đến việc nhận thông báo khi một hành động xảy ra nên đăng ký với một sự kiện và xử lý nó.

Trong C#, một sự kiện là encapsulated delegate. Nó phụ thuộc vào delegate. Delegate xác định chữ ký cho phương thức xử lý event của subscriber class.

Hình dưới đây minh họa event trong C#.

Khai báo một Event

Một event có thể được khai báo trong 2 bước:

  • Khai báo một delegate.
  • Khai báo một biến của delegate với từ khóa event.

Theo dõi ví dụ dưới đây để xem cách khai báo một event trong publisher class.

// Example: Declaring an Event
public delegate void Notify();  // delegate
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // event
}

Trong ví dụ trên, chúng tôi đã khai báo một delegate Notify và sau đó khai báo một event ProcessCompleted thuộc loại delegate Notify bằng cách sử dụng từ khóa “event” trong lớp ProcessBusinessLogic. Vì vậy, lớp ProcessBusinessLogic được gọi là publisher. The Notify delegate chỉ định chữ ký cho ProcessCompleted event handler. Nó chỉ định rằng event handler method trong subscriber class phải có kiểu trả về void và không có tham số.

Bây giờ, hãy xem cách raise event ProcessCompleted. Hãy xem xét việc thực hiện sau đây.

//Example: Raising an Event
public delegate void Notify();  // delegate
                    
public class ProcessBusinessLogic
{
    public event Notify ProcessCompleted; // event
    public void StartProcess()
    {
        Console.WriteLine("Process Started!");
        // some code here..
        OnProcessCompleted();
    }
    protected virtual void OnProcessCompleted() //protected virtual method
    {
        //if ProcessCompleted is not null then call delegate
        ProcessCompleted?.Invoke(); 
    }
}

Ở trên, phương thức StartProcess() gọi phương thức onProcessCompleted() ở cuối để raise một event. Thông thường, để đưa ra một sự kiện, protected and virtual method phải được xác định bằng tên On< EventName>. Các lớp dẫn xuất được bảo vệ và ảo cho phép ghi đè logic để raising the event. Tuy nhiên, lớp dẫn xuất phải luôn gọi phương thức On< EventName> của lớp cơ sở để đảm bảo rằng các delegates đã đăng ký nhận được event.

Phương thức OnProcessCompleted() gọi delegate bằng cách sử dụng ProcessCompleted?.Invoke();. Điều này sẽ gọi tất cả các phương thức xử lý sự kiện đã đăng ký với sự kiện ProcessCompleted.

Subscriber class phải đăng ký event ProcessCompleted và xử lý nó bằng phương thức có chữ ký khớp với Notify delegate, như hiển thị bên dưới.

// Example: Consume an Event
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }
    // event handler
    public static void bl_ProcessCompleted()
    {
        Console.WriteLine("Process Completed!");
    }
}

Ở trên, Program class là người đăng ký sự kiện ProcessCompleted. Nó đăng ký với sự kiện bằng toán tử +=. Hãy nhớ rằng, đây cũng giống như cách chúng ta thêm các phương thức vào danh sách gọi của multicast delegate. Phương thức bl_ProcessCompleted() xử lý sự kiện vì nó khớp với chữ ký của Notify delegate.

Built-in EventHandler Delegate

.NET Framework bao gồm các built-in delegate types EventHandler và EventHandler< TEventArgs> cho các sự kiện phổ biến nhất. Thông thường, bất kỳ sự kiện nào cũng phải bao gồm hai thông số: nguồn của sự kiện và dữ liệu sự kiện. Sử dụng EventHandler delegate cho tất cả các sự kiện không bao gồm dữ liệu sự kiện. Sử dụng EventHandler< TEventArgs> delegate cho các sự kiện bao gồm dữ liệu được gửi đến trình xử lý.

Ví dụ hiển thị ở trên có thể sử dụng EventHandler delegate mà không cần khai báo custom Notify delegate, như hiển thị bên dưới.

// Example: EventHandler
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }
    // event handler
    public static void bl_ProcessCompleted(object sender, EventArgs e)
    {
        Console.WriteLine("Process Completed!");
    }
}
public class ProcessBusinessLogic
{
    // declaring an event using built-in EventHandler
    public event EventHandler ProcessCompleted; 
    public void StartProcess()
    {
        Console.WriteLine("Process Started!");
        // some code here..
        OnProcessCompleted(EventArgs.Empty); //No event data
    }
    protected virtual void OnProcessCompleted(EventArgs e)
    {
        ProcessCompleted?.Invoke(this, e);
    }
}

Trong ví dụ trên, event handler bl_ProcessCompleted() bao gồm hai tham số khớp với EventHandler delegate.

Ngoài ra, chuyển thông tin này với tư cách là người gửi và EventArgs.Empty, khi chúng tôi đưa ra một sự kiện bằng cách sử dụng Invoke() trong phương thức OnProcessCompleted().

Vì chúng tôi không cần bất kỳ dữ liệu nào cho sự kiện của mình nên nó chỉ thông báo cho người đăng ký về việc hoàn tất quy trình và vì vậy chúng tôi đã chuyển EventArgs.Empty.

// Passing an Event Data
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }
    // event handler
    public static void bl_ProcessCompleted(object sender, bool IsSuccessful)
    {
        Console.WriteLine("Process " + (IsSuccessful? "Completed Successfully": "failed"));
    }
}
public class ProcessBusinessLogic
{
    // declaring an event using built-in EventHandler
    public event EventHandler<bool> ProcessCompleted; 
    public void StartProcess()
    {
        try
        {
            Console.WriteLine("Process Started!");
			
            // some code here..
            OnProcessCompleted(true);
        }
        catch(Exception ex)
        {
            OnProcessCompleted(false);
        }
    }
    protected virtual void OnProcessCompleted(bool IsSuccessful)
    {
        ProcessCompleted?.Invoke(this, IsSuccessful);
    }
}

Trong ví dụ trên, chúng ta chuyển một giá trị boolean duy nhất cho các trình xử lý để cho biết liệu quá trình có hoàn thành thành công hay không.

Nếu bạn muốn chuyển nhiều giá trị dưới dạng dữ liệu sự kiện, hãy tạo một lớp bắt nguồn từ lớp cơ sở EventArgs, như hiển thị bên dưới.

// Example: Custom EventArgs Class
class ProcessEventArgs : EventArgs
{
    public bool IsSuccessful { get; set; }
    public DateTime CompletionTime { get; set; }
}

Ví dụ sau đây cho biết cách chuyển lớp ProcessEventArgs tùy chỉnh cho trình xử lý.

// Example: Passing Custom EventArgs
class Program
{
    public static void Main()
    {
        ProcessBusinessLogic bl = new ProcessBusinessLogic();
        bl.ProcessCompleted += bl_ProcessCompleted; // register with an event
        bl.StartProcess();
    }
    // event handler
    public static void bl_ProcessCompleted(object sender, ProcessEventArgs e)
    {
        Console.WriteLine("Process " + (e.IsSuccessful? "Completed Successfully": "failed"));
        Console.WriteLine("Completion Time: " + e.CompletionTime.ToLongDateString());
    }
}
public class ProcessBusinessLogic
{
    // declaring an event using built-in EventHandler
    public event EventHandler<ProcessEventArgs> ProcessCompleted; 
    public void StartProcess()
    {
        var data = new ProcessEventArgs();
		
        try
        {
            Console.WriteLine("Process Started!");
			
            // some code here..
            
            data.IsSuccessful = true;
            data.CompletionTime = DateTime.Now;
            OnProcessCompleted(data);
        }
        catch(Exception ex)
        {
            data.IsSuccessful = false;
            data.CompletionTime = DateTime.Now;
            OnProcessCompleted(data);
        }
    }
    protected virtual void OnProcessCompleted(ProcessEventArgs e)
    {
        ProcessCompleted?.Invoke(this, e);
    }
}

Do đó, bạn có thể create, raise, register, and handle events trong C#.

Những điểm cần nhớ:

  • Một event là một trình bao bọc xung quanh một delegate. Nó phụ thuộc vào delegate.
  • Sử dụng từ khóa “event” với biến delegate type để khai báo một event.
  • Sử dụng built-in delegate EventHandler hoặc EventHandler< TEventArgs> cho các common events.
  • Publisher class raises một event và subscriber class đăng ký một event và cung cấp event-handler method.
  • Đặt tên cho phương thức raises một event có tiền tố “On” cùng với tên event.
  • Signature của handler method phải khớp với delegate signature.
  • Đăng ký một event bằng toán tử +=. Hủy đăng ký bằng toán tử -=. Không thể sử dụng toán tử =.
  • Truyền event data bằng EventHandler<TEventArgs>.
  • Lấy lớp cơ sở EventArgs để tạo lớp dữ liệu sự kiện tùy chỉnh.
  • Các events có thể được khai báo là static, virtual, sealed, and abstract.
  • Một giao diện có thể bao gồm event với tư cách thành viên.
  • Event handlers được gọi đồng bộ nếu có multiple subscribers.
Avatar photo

Clean Code: Nguyên tắc viết hàm trong lập trình…

Trong quá trình phát triển phần mềm, việc viết mã nguồn dễ đọc, dễ hiểu là yếu tố then chốt để đảm bảo code...
Avatar photo Dat Tran Thanh
3 min read

Clean Code: Nguyên tắc comment trong lập trình

Trong lập trình, code không chỉ là một tập hợp các câu lệnh để máy tính thực thi, mà còn là một hình thức...
Avatar photo Dat Tran Thanh
3 min read

Clean Code: Nguyên tắc xử lý lỗi (Error Handling)

Trong quá trình phát triển phần mềm, việc xử lý lỗi không chỉ là một phần quan trọng mà còn ảnh hưởng trực tiếp...
Avatar photo Dat Tran Thanh
4 min read

Leave a Reply

Your email address will not be published. Required fields are marked *