Trong bài viết này, chúng ta sẽ cùng tìm hiểu về Collection và Generic trong lập trình hướng đối tượng C#.
#1/2. Làm việc với Collection
– Collection là một tập hợp các dữ liệu không cùng kiểu, ví dụ khi khai báo ArrayList arrList = new ArrayList() { 1, “5”, 2.5, true };. Collection cung cấp rất nhiều các method giúp lập trình viên thao tác với dữ liệu một cách đơn giản và dễ dàng.
– Một trong những ưu điểm lớn nhất của Collection là khả năng tương tác và thay đổi dữ liệu bên trong nó ngay tại thời điểm chạy (runtime).
– Collection thuộc thư viện System.Collections
– Các lớp Collection là các lớp đặc biệt để lưu trữ và thu hồi dữ liệu. Những lớp này cung cấp sự hỗ trợ cho Stack, Queue, List, Hashtable.
– Đa số các lớp Collection trong C# triển khai cùng các Interface.
– Trong C#, các lớp Collection được sử dụng cho nhiều mục đích khác nhau như cấp phát bộ nhớ động cho các phần tử, hay truy cập một danh sách các item dựa trên chỉ mục,…
Các Collection phổ biến:
Tên class | Ý nghĩa |
ArrayList Generic Collection: List<> |
Truy cập phần tử (item) theo Chỉ mục. Nó là một sự thay thế cho mảng. Không giống mảng, ArrayList có thể thêm, xóa item thông qua chỉ mục và chính nó có thể tự điều chỉnh size một cách tự động. Ngoài ra, nó cũng cho phép cấp phát bộ nhớ động, thêm, tìm kiếm, sắp xếp,… các item trong một list. |
Hashtable Generic Collection: Dictionary<> DictionaryEntry |
Truy cập phần tử (item) theo Key. Lớp Hashtable sử dụng một cặp Key-Value để truy cập các item trong collection. Mỗi item trong Hashtable được truy cập bởi Key và sẽ có một Value tương ứng.. |
SortedList Generic Collection: SortedList<> |
Truy cập phần tử (item) theo Key, cũng như Chỉ mục để truy các item trong một list. Một SortedList là sự tổ hợp của một mảng và một Hashtable. Nếu truy cập item bằng cách sử dụng chỉ mục thì nó là một ArrayList, còn nếu access bằng key thì nó là một Hashtable. Tập hợp các item LUÔN LUÔN được SẮP XẾP theo giá trị KEY. |
Stack | Lớp Stack trong C# biểu diễn một tập hợp LIFO (Last In, First Out) của các đối tượng. Nó được sử dụng khi muốn truy cập các item theo dạng LIFO. Khi thêm một item vào danh sách thì được gọi là pushing, còn khi remove một item thì gọi là popping. Stack tức là ngăn xếp, push: thêm item mới vào đỉnh stack, pop: thao tác lấy một phần tử từ đỉnh stack. |
Queue | Lớp Queue sẽ truy cập các item theo dạng FIFO (First In, First Out). Khi thêm một item vào danh sách thì được gọi là enqueue, còn khi remove một item thì gọi là deque. |
BitArray | Nó biểu diễn một mảng ở dạng biểu diễn nhị phân bởi sử dụng các giá trị 1 và 0. Nó được sử dụng khi bạn cần lưu giữ các Bit nhưng không biết trước số lượng Bit. Bạn có thể truy cập các item từ BitArray collection bởi sử dụng một chỉ mục là số nguyên, mà bắt đầu từ 0. |
#2/2. Làm việc với Generic
– Generic được giới thiệu từ C# 2.0. Generic cho phép bạn xác định một class với trình giữ chỗ (placeholders) cho fields, methods, parameters,… Generic thay thế các placeholders này bằng một số loại dữ liệu cụ thể tại thời gian biên dịch (compile time). Có nghĩa là, generic là tính năng cho phép bạn trì hoãn chỉ ra kiểu dữ liệu khi khai báo classes hoặc methods,… cho đến khi chương trình được thực thi.
– Generic được định nghĩa bằng cách sử dụng dấu ngoặc nhọn <>. Trong .NET có sẵn trong namespace: System.Collections.Generic
– Generic cũng là một kiểu dữ liệu trong C# như int, string, bool,… nhưng nó là một kiểu dữ liệu “tự do”, tùy vào mục đích sử dụng mà nó có thể đại diện cho tất cả các kiểu dữ liệu còn lại.
– Đặc điểm của generic:
- Giúp tối đa hóa sử dụng lại code (chỉ cần viết 1 hàm có thể tái sử dụng cho nhiều kiểu dữ liệu), an toàn và tăng tốc độ.
- Có thể dùng generic với: interfaces, classes, methods, events, delegates, structs.
- Có thể tạo generic class với ràng buộc cho các method trong class.
- Bạn có thể lấy thông tin của kiểu dữ liệu được sử dụng bởi generic ở thời điểm runtime bằng Reflection.
– Sau đây là các generic collections được sử dụng rộng rãi:
Generic Collections | Mô tả |
List<T> | Chứa các elements của loại được chỉ định. Kích thước sẽ tăng lên tự động khi bạn thêm các elements vào danh sách. |
Dictionary<TKey,TValue> | Chứa các cặp key-value. |
Hashset<T> | Chứa các phần tử không trùng lặp. |
SortedList<TKey,TValue> | Lưu trữ các cặp key và value. Nó tự động thêm các elements theo thứ tự tăng dần, mặc định sắp xếp theo key. |
Stack<T> | Lưu trữ các giá trị dưới dạng LIFO (Last In First Out). Nó cung cấp phương thức Push() để thêm giá trị và phương thức Pop(), Peek() để truy xuất giá trị. |
Queue<T> | Lưu trữ các giá trị theo kiểu FIFO (First In First Out). Nó giữ thứ tự mà các giá trị đã được thêm vào. Nó cung cấp một phương thức Enqueue() để thêm các giá trị và phương thức Dequeue() để lấy các giá trị từ collection. |
Ví dụ 1/2:
Sau đây là một generic class đơn giản với một generic field, generic property và generic method.
using System; namespace MinhHoangBlog { class Program { static void Main(string[] args) { GenericClass<int> intGenericClass = new GenericClass<int>(86); intGenericClass.genericProperty = 2017; int intVal = intGenericClass.genericMethod(2019); GenericClass<string> strGenericClass = new GenericClass<string>("Welcome to"); strGenericClass.genericProperty = "Minh Hoàng Blog"; string strVal = strGenericClass.genericMethod("www.minhhn.com"); Console.ReadKey(); } } class GenericClass<T> { private T genericField; public T genericProperty { get; set; } // Constructor public GenericClass(T val) { genericField = val; } public T genericMethod(T genericParameter) { T rtn = default(T); Console.WriteLine("Field type: {0}, value: {1}", typeof(T).ToString(), genericField); Console.WriteLine("Property type: {0}, value: {1}", typeof(T).ToString(), genericProperty); Console.WriteLine("Parameter type: {0}, value: {1}", typeof(T).ToString(), genericParameter); Console.WriteLine("Return type: {0}", typeof(T).ToString()); return rtn; } } }
・Như bạn có thể thấy trong đoạn mã trên, GenericClass được định nghĩa bằng <T>. <> chỉ ra rằng GenericClass là chung chung và loại dữ liệu cụ thể sẽ được xác định sau, bây giờ tạm thời hãy xem nó là T. Bạn có thể lấy bất kỳ ký tự nào thay vì dùng chữ T.
・Bây giờ, trình biên dịch gán loại dữ liệu dựa trên loại dữ liệu được người gọi truyền qua khi khởi tạo một lớp GenericClass. Ở đây trong hàm main(), chúng ta truyền 2 loại dữ liệu khác nhau là int và string:
Ví dụ 2/2:
Sau đây sẽ minh họa code hoán vị sử dụng generic, bạn có thể hoán vị số hoặc chuỗi. Nhờ có generic giúp chúng ta giảm thiểu việc code và tăng tính tái sử dụng.
Xem thêm: Cách truyền tham trị, tham chiếu trong C#.
using System; namespace MinhHoangBlog { class Program { static void Main(string[] args) { int x = 6; int y = 8; Console.WriteLine("Trước khi hoán vị: x = {0}, y = {1}", x, y); HoanVi<int>(ref x, ref y); Console.WriteLine("Sau khi hoán vị: x = {0}, y = {1}", x, y); string str1 = "Minh Hoàng Blog"; string str2 = "www.minhhn.com"; Console.WriteLine("Trước khi hoán vị: str1 = {0}, str2 = {1}", str1, str2); HoanVi<string>(ref str1, ref str2); Console.WriteLine("Sau khi hoán vị: str1 = {0}, str2 = {1}", str1, str2); Console.ReadKey(); } static void HoanVi<T>(ref T a, ref T b) { T tmp = a; a = b; b = tmp; } } }
[…] Xem thêm: Làm việc với Collection và Generic trong C#. […]
[…] niệm IEnumerable thì chắc nhiều bạn biết. Các kiểu collection trong C# như List, ArrayList, Dictionary,… đều implement interface IEnumerable, do đó […]