Menu
CoddyTech

Generics (lớp và phương thức)

Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 42 trên 70.

Generic cho phép bạn viết các lớp và phương thức hoạt động với mọi kiểu dữ liệu mà vẫn đảm bảo an toàn kiểu. Thay vì viết các lớp riêng cho IntList, StringList và DoubleList, bạn chỉ cần viết một lớp generic xử lý tất cả.

Lớp tổng quát sử dụng một tham số kiểu, thường được đặt tên là T và đặt trong dấu ngoặc nhọn:

public class Box<T>
{
    private T content;
    
    public void Store(T item) => content = item;
    public T Retrieve() => content;
}

// Cách dùng với các kiểu dữ liệu khác nhau
Box<int> intBox = new Box<int>();
intBox.Store(42);

Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");

Khi tạo Box<int>, trình biên dịch thay mọi T bằng int. Cách này giúp kiểm tra kiểu dữ liệu ngay khi biên dịch, bạn không thể vô tình lưu chuỗi vào hộp int.

Phương thức generic (generic method) hoạt động tương tự, cho phép một phương thức xử lý nhiều kiểu dữ liệu khác nhau:

public static void Swap<T>(ref T a, ref T b)
{
    T temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);  // x=2, y=1

Trình biên dịch suy luận kiểu dữ liệu từ các đối số, nên bạn không cần viết tường minh Swap<int>. Kiểu tổng quát là nền tảng của các cấu trúc tập hợp như List<T> và Dictionary<TKey, TValue>, giúp bạn viết code có thể tái sử dụng và an toàn về kiểu dữ liệu.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống Storage<T> tổng quát có thể chứa và quản lý các phần tử thuộc mọi kiểu. Bạn sẽ tạo một lớp vùng chứa có thể tái sử dụng để minh họa sức mạnh của kiểu tổng quát: viết một lần, dùng với số nguyên, chuỗi hoặc bất kỳ kiểu nào khác!

Bạn sẽ sắp xếp code trong hai tệp:

  • Storage.cs: Tạo một lớp Storage<T> tổng quát trong không gian tên DataStorage. Vùng chứa lưu trữ của bạn cần:
    • Có một mảng kiểu T ở phạm vi riêng tư để chứa các phần tử và thuộc tính Count theo dõi số phần tử đang được lưu trữ
    • Nhận sức chứa trong hàm khởi tạo để khởi tạo mảng bên trong
    • Có phương thức Add(T item) để thêm một phần tử nếu còn chỗ
    • Có phương thức Get(int index) để trả về phần tử tại chỉ số được chỉ định
    • Có phương thức GetAll() để trả về tất cả phần tử đã lưu trữ, nối chúng bằng ", "
  • Program.cs: Trong tệp chính, hãy minh họa cách lớp tổng quát hoạt động với hai kiểu khác nhau. Tạo một Storage<int> và một Storage<string>, thêm các phần tử vào từng vùng chứa và cho thấy cùng một lớp hoạt động trơn tru với cả hai kiểu.

Bạn sẽ nhận bốn đầu vào:

  • Số nguyên thứ nhất cần lưu trữ
  • Số nguyên thứ hai cần lưu trữ
  • Chuỗi thứ nhất cần lưu trữ
  • Chuỗi thứ hai cần lưu trữ

Tạo một vùng chứa số nguyên có sức chứa 5 và một vùng chứa chuỗi có sức chứa 5. Thêm hai số nguyên vào vùng chứa số nguyên và hai chuỗi vào vùng chứa chuỗi.

In đầu ra theo định dạng này:

Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}

Ví dụ, nếu đầu vào là 42, 100, Hello và World, đầu ra sẽ là:

Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: Hello

Lưu ý rằng bạn chỉ cần viết lớp Storage<T> một lần, nhưng lớp này hoạt động hoàn hảo với cả int và string. Đó chính là điều kỳ diệu của kiểu tổng quát! Trình biên dịch đảm bảo an toàn kiểu dữ liệu, vì vậy bạn không thể vô tình thêm một chuỗi vào vùng chứa số nguyên.

Tự làm thử

using System;
using DataStorage;

class Program
{
    public static void Main(string[] args)
    {
        // Đọc dữ liệu đầu vào
        int num1 = Convert.ToInt32(Console.ReadLine());
        int num2 = Convert.ToInt32(Console.ReadLine());
        string str1 = Console.ReadLine();
        string str2 = Console.ReadLine();

        // TODO: Tạo Storage<int> có sức chứa 5
        
        // TODO: Thêm hai số nguyên vào nơi lưu trữ số nguyên
        
        // TODO: Tạo Storage<string> có sức chứa 5
        
        // TODO: Thêm hai chuỗi vào nơi lưu trữ chuỗi
        
        // TODO: In đầu ra theo định dạng yêu cầu
        // Int storage: {GetAll()}
        // Số lượng số nguyên: {Count}
        // Số nguyên đầu tiên: {Get(0)}
        // String storage: {GetAll()}
        // String count: {Count}
        // First string: {Get(0)}
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C# online