Khi phát triển các ứng dụng phức tạp yêu cầu quản lý trạng thái danh sách động, việc sử dụng các cấu trúc dữ liệu tiêu chuẩn đôi khi không đáp ứng đủ nhu cầu về khả năng giám sát biến đổi. Trong hệ sinh thái của .NET, lớp Collection<T> thuộc thư viện System.Collections.ObjectModel cung cấp một cơ chế mạnh mẽ cho phép kế thừa và tùy chỉnh hành vi của mảng, đồng thời hỗ trợ tích hợp sẵn các sự kiện thông báo khi có thao tác trên tập hợp.
Phương pháp tiếp cận hiệu quả là mở rộng lớp Collection<T> bằng cách ghi đè các phương thức ảo cốt lõi như InsertItem, RemoveItem, ClearItems và SetItem. Khi các phương thức này được gọi bởi lớp gốc, chúng sẽ kích hoạt logic định nghĩa trước đó, cho phép gửi thông tin đến các đối tượng lắng nghe thông qua cơ chế delegate.
Cấu hình Các Loại Sự Kiện
Trước khi xây dựng lớp tập hợp chính, cần định nghĩa một kiểu liệt kê (enum) để phân loại các thao tác xảy ra, và một lớp kế thừa từ EventArgs để truyền tải chi tiết thay đổi vào trình xử lý sự kiện.
using System;
using System.Collections.ObjectModel;
namespace CustomNotifiableCollections
{
// Định nghĩa các loại thao tác có thể xảy ra trên tập hợp
public enum OperationType
{
ItemAdded,
ItemRemoved,
ItemReplaced,
CollectionCleared
}
// Đối tượng chứa thông tin chi tiết về sự thay đổi
public class ItemChangedEventArgs : EventArgs
{
public readonly string Index;
public readonly string OldValue;
public readonly string NewValue;
public readonly OperationType Type;
public ItemChangedEventArgs(string index, string oldVal, string newVal, OperationType type)
{
Index = index;
OldValue = oldVal;
NewValue = newVal;
Type = type;
}
}
}
Tạo Lớp Tập Hợp Kế Thừa
Lớp dưới đây kế thừa từ Collection<string> và định nghĩa sự kiện OnChanged. Các phương thức ghi đè đảm bảo rằng sau khi cập nhật dữ liệu nội bộ, sự kiện sẽ được kích hoạt nếu có người đăng ký lắng nghe.
public class StringDataStore : Collection<string>
{
// Khai báo sự kiện thông báo
public event EventHandler<ItemChangedEventArgs> OnChanged;
protected override void InsertItem(int index, string item)
{
// Thực hiện thêm vào danh sách gốc
base.InsertItem(index, item);
// Kích hoạt sự kiện thông báo
RaiseChangeEvent(OperationType.ItemAdded, index.ToString(), null, item);
}
protected override void SetItem(int index, string newItem)
{
// Lưu giá trị cũ trước khi thay thế
string oldValue = this[index];
base.SetItem(index, newItem);
// Kích hoạt sự kiện thay thế
RaiseChangeEvent(OperationType.ItemReplaced, index.ToString(), oldValue, newItem);
}
protected override void RemoveItem(int index)
{
string removedValue = Items[index];
base.RemoveItem(index);
RaiseChangeEvent(OperationType.ItemRemoved, index.ToString(), removedValue, null);
}
protected override void ClearItems()
{
base.ClearItems();
RaiseChangeEvent(OperationType.CollectionCleared, null, null, null);
}
// Phương thức tiện ích để kiểm tra sự an toàn của handler
private void RaiseChangeEvent(OperationType opType, string idx, string oldVal, string newVal)
{
var handler = OnChanged;
if (handler != null)
{
handler(this, new ItemChangedEventArgs(idx, oldVal, newVal, opType));
}
}
}
Ứng Dụng Và Xử Lý Lắng Nghe
Sau khi định nghĩa lớp tập hợp tùy chỉnh, quá trình sử dụng sẽ diễn ra tương tự như một đối tượng bình thường, nhưng kèm theo cơ chế gắn kết sự kiện (Event Wiring).
static void Main(string[] args)
{
// Khởi tạo bộ lưu trữ dữ liệu
var dataStore = new StringDataStore();
// Gắn hàm xử lý sự kiện
dataStore.OnChanged += HandleCollectionUpdate;
// Thêm các mục mới
dataStore.Add("Dinosaurl A");
dataStore.Add("Dinosaur B");
dataStore.Add("Dinosaur C");
// Cập nhật mục tại chỉ số 1
dataStore[1] = "Updated Dinosaur B";
// Xóa mục tại vị trí đầu tiên
dataStore.RemoveAt(0);
Console.Read();
}
private static void HandleCollectionUpdate(object sender, ItemChangedEventArgs e)
{
switch (e.Type)
{
case OperationType.ItemAdded:
Console.WriteLine($"Đã thêm: {e.NewValue} tại vị trí {e.Index}");
break;
case OperationType.ItemReplaced:
Console.WriteLine($"Thay thế: {e.OldValue} thành {e.NewValue} tại {e.Index}");
break;
case OperationType.ItemRemoved:
Console.WriteLine($"Đã xóa: {e.OldValue} tại vị trí {e.Index}");
break;
case OperationType.CollectionCleared:
Console.WriteLine("Toàn bộ danh sách đã bị xóa sạch");
break;
}
}
Kỹ thuật này cho phép tách biệt hoàn toàn giữa logic quản lý dữ liệu và logic giao diện/ngôn ngữ khác. Nhờ việc ghi đè các phương thức ảo của lớp gốc, mọi thay đổi dù trực tiếp hay gián tiếp đều có thể được phát hiện mà không cần phải gọi thủ công phương thức gửi sự kiện sau mỗi dòng mã.