Networks Business Online Việt Nam & International VH2

Event trong C# các Event của .Net

Đăng ngày 07 November, 2022 bởi admin

Event trong C#

Các sự kiện (Event) là cơ chế để một đối tượng (đối tượng của lớp) này thông báo đến
đối tượng khác có điều gì đó mà lớp khác quan tâm vừa xảy ra.
Lớp mà từ đó gửi đi sự kiện gọi tên nó là publisher
và các lớp nhận được sự kiện gọi là là các subsriber.

Để làm được việc này nó hoạt động giống hệt cơ chế delegate,
thực tế là trong .NET các Event xây dựng với nền tảng chính là delegate,
nên trước khi tìm hiểu Event hay vào tìm hiểu
delegate trước.

Vậy có yếu tố gì khi sử dụng delegate mà phải thêm khái niệm Event, hãy tìm hiểu và khám phá trường hợp sau

Vấn đề của delegate và sự giải quyết của Event

Ví dụ dưới đây sẽ dùng delegate (đã biết ở phần trước) để xây dựng cơ chế để một lớp này đăng ký nhận sự kiện từ một lớp khác

DelegateEvent. cs

using System;

namespace CS009_Event {

    /*
        Publisher là lớp phát đi sự kiện, bằng cách gọi
        một deleage trong phương thức Send
    */
    public class Publisher {
        public delegate void NotifyNews (object data);

        public NotifyNews event_news;

        public void Send () {
            event_news?.Invoke ("Co tin moi");
        }
    }

    // SubscriberA lớp này đăng ký nhận sự kiện từ Publisher,
    // bằng phương thức Sub, khi sự kiện xảy ra nó sẽ gọi ReceiverFromPublisher
    public class SubscriberA {
        public void Sub (Publisher p) {
            p.event_news += ReceiverFromPublisher;
        }

        void ReceiverFromPublisher (object data) {
            Console.WriteLine ("SubscriberA: " + data.ToString ());
        }
    }

    // SubscriberA lớp này đăng ký nhận sự kiện từ Publisher,
    // bằng phương thức Sub - khi đăng ký nó hủy việc nhận sự kiện của các đối tượng khác,
    // khi sự kiện xảy ra nó sẽ gọi ReceiverFromPublisher
    public class SubscriberB {
        public void Sub (Publisher p) {
            p.event_news = null;  // Hủy các đối tượng khác nhận sự kiện
            p.event_news += ReceiverFromPublisher;
        }

        void ReceiverFromPublisher (object data) {
            Console.WriteLine ("SubscriberB: " + data.ToString ());
        }
    }

}

Khi vận dụng trong lớp Program

static void TestDelegate()
{
    Publisher p = new Publisher();
    SubscriberA sa = new SubscriberA();
    SubscriberB sb = new SubscriberB();

    sa.Sub(p);
    sb.Sub(p);

    p.Send();
}

Khi chạy thử – gọi hàm TestDelegate, thì kết quả in ra

SubscriberB: Co tin moi

Phân tích yếu tố của đoạn mã trên :

Lớp Publisher xây dựng một delegate có tên NotifyNews và khai báo thuộc tính event_news triển khai nó, khi Publisher thi hành Send() nó sẽ thi hành delegate này và như vậy những đối tượng nào đăng ký vào delegate sẽ có cơ hội nhận thông tin mới từ Publisher

Hai lớp SubscriberASubscriberB tiến hành đăng ký phương thức ReceiverFromPublisher vào Delegate của Pushisher, và như vậy khi chạy code đã có kết quả như trên.

Tuy nhiên, nhìn vào phương thức public void Sub(Publisher p) của SubscriberB thì đoạn mã:

p.event_news = null;

Nó đã gán event_news bằng null, có nghĩa là việc đăng ký của SubcriberA lúc trước bị loại bỏ bởi SubcriberB, dẫn tới chỉ có SubcriberB nhận được tin mới. Điều này là phá hỏng nguyên tắc hoạt động của mô hình lập trình sự kiện – phá vỡ sự đóng gói

Để giải quyết vấn đề trên, thật đơn giản với .NET chỉ cần thêm từ khóa event vào định nghĩa event_news của Pushliser, và từ đây event_news gọi là Event chứ không còn gọi là Delegate

public event NotifyNews event_news;

Từ lúc này, các Subscriber chỉ có thể đăng ký nhận sự kiện với toán tử += hoặc hủy nhận sự kiện với toán tử -= chứ không thể thực hiện gán p.event_news = null vì nếu viết code như vậy lập tức báo lỗi.

Tóm lại, Event là Delegate nhưng khi khai báo thêm từ khóa event, dẫn tới chỉ có thể thực hiện toán tử += hoặc -= với Event

Event trong thư viện .NET

Các Event ví dụ như KeyDown, GotFocus, Load của Form,
Application.ApplicationExit, Application.Idle
đều xây dựng từ một delegate có tên EventHandler, nó đã định nghĩa sẵn có trong thư viện .NET với dạng:

public delegate void EventHandler(object sender?, EventArgs e);
public delegate void EventHandler(object sender?, TEventArgs e);

Như vậy bạn có thể sử dụng luôn delegate
EventHandler để xây dựng các Event của riêng mình sử dụng cho các Publisher,
chỉ cần xây dựng các lớp phái sinh từ EventArgs
với mục đích thêm vào các tham số riêng khi gửi sử kiện.
Ví dụ:

UseEventHandler. cs

using System;

namespace CS009 {

  // Xây dựng lớp MyEventArgs kế thừa từ EventArgs
  public class MyEventArgs : EventArgs {
    public MyEventArgs (string data) {
      this.data = data;
    }
    // Lưu dữ liệu gửi đi từ publisher
    private string data;

    public string Data {
      get { return data; }
    }
  }

  // Xây dựng lớp, phát đi sự kiện (data)
  public class ClassA {
    // Tạo Event với EventHandler
    public event EventHandler event_news;

    public void Send () {
      event_news?.Invoke (this, new MyEventArgs ("Có tin mới Abc ..."));
    }
  }

  public class ClassB {
    public void Sub (ClassA p)
    {
      p.event_news += ReceiverFromPublisher;
    }

    private void ReceiverFromPublisher (object sender, MyEventArgs e)
    {
      Console.WriteLine ("ClassB: " + e.Data);
    }
  }


  public class ClassC {
    public void Sub (ClassA p)
    {
      p.event_news += ReceiverFromPublisher;
    }

    private void ReceiverFromPublisher (object sender, MyEventArgs e)
    {
      Console.WriteLine ("ClassC: " + e.Data);
    }
  }

}

Khi sử dụng

static void TestEventHandler ()
{
    ClassA p  = new ClassA();
    ClassB sa = new ClassB();
    ClassC sb = new ClassC();

    sa.Sub (p); // sa đăng ký nhận sự kiện từ p
    sb.Sub (p); // sb đăng ký nhận sự kiện từ p

    p.Send ();
}

Kết quả chạy

ClassB: Có tin mới Abc ...
ClassC: Có tin mới Abc ...

Lưu ý: lớp MyEventArgs xây dựng kế thừa từ EventArgs ở trên
với mục đích chuyên trở thêm dữ liệu,
để đảm bảo dữ liệu không bị sửa đổi bởi các Subsriber thì data
xây dựng theo cách thức như trên để chỉ có thể đọc! (sau khi khởi tạo từ hàm khởi tạo)

Source code:
CS009_Event (Git),
hoặc tải cs009-event

ĐĂNG KÝ KÊNH, XEM CÁC VIDEO TRÊN XUANTHULAB

Đăng ký nhận bài viết mới

Source: https://vh2.com.vn
Category : Tin Học