Trong lập trình C#, Generics là một trong những tính năng quan trọng và mạnh mẽ nhất. Generics cho phép chúng ta viết mã linh hoạt và tái sử dụng hơn bằng cách cho phép chúng ta tạo ra các lớp, phương thức và cấu trúc dữ liệu mà có thể làm việc với bất kỳ kiểu dữ liệu nào, thay vì chỉ hoạt động với một kiểu cụ thể.

1. Generics trong C# là gì?

Generics trong C# là một tính năng quan trọng cho phép chúng ta viết mã linh hoạt và tái sử dụng bằng cách làm việc với các kiểu dữ liệu chưa biết trước (generic types). Thay vì viết mã cụ thể cho từng kiểu dữ liệu riêng biệt, chúng ta có thể viết mã một cách tổng quát để hoạt động với một loạt các kiểu dữ liệu khác nhau.

2. Cú pháp cơ bản của Generics trong C#

Để định nghĩa một generic class hoặc method trong C#, chúng ta sử dụng cú pháp sau:

class ClassName<T>
{
    // Code của lớp generic
}
// Hoặc
public T MethodName<T>(T parameter)
{
    // Code của method generic
}

Trong đó, T là một tham số kiểu generic, chúng ta có thể đặt tên bất kỳ cho nó. T chỉ ra kiểu dữ liệu chưa biết được sử dụng trong class hoặc method.

>>>>> Xem thêm bài viết tương tự tại đây:

3. Ví dụ về Generic Class trong C#

Hãy xem xét một ví dụ đơn giản về một generic class Stack<T> (ngăn xếp) trong C#, nó cho phép lưu trữ và truy cập các phần tử của bất kỳ kiểu dữ liệu nào:

public class Stack<T>
{
    private T[] elements;
    private int top = 0;
    public Stack(int size)
    {
        elements = new T[size];
    }
    public void Push(T item)
    {
        if (top < elements.Length)
            elements[top++] = item;
        else
            throw new StackOverflowException("Stack is full!");
    }
    public T Pop()
    {
        if (top > 0)
            return elements[--top];
        else
            throw new InvalidOperationException("Stack is empty!");
    }
}

Hoặc ta xem thêm một ví dụ khác về generic class Pair<T> trong C#, nó cho phép lưu trữ một cặp giá trị có cùng kiểu dữ liệu:

public class Pair<T>
{
    private T first;
    private T second;
    public Pair(T first, T second)
    {
        this.first = first;
        this.second = second;
    }
    public T GetFirst()
    {
        return first;
    }
    public T GetSecond()
    {
        return second;
    }
}

4. Sử dụng Generic Class trong C#

Bây giờ chúng ta có thể sử dụng lớp generic Stack<T> để lưu trữ và truy cập các phần tử của bất kỳ kiểu dữ liệu nào:

Stack<int> intStack = new Stack<int>(5);
intStack.Push(10);
intStack.Push(20);
intStack.Push(30);
Console.WriteLine(intStack.Pop()); // In ra: 30
Stack<string> stringStack = new Stack<string>(3);
stringStack.Push("Hello");
stringStack.Push("World");
Console.WriteLine(stringStack.Pop()); // In ra: World

Hoặc chúng ta có thể sử dụng lớp generic Pair<T> để lưu trữ và truy cập các cặp giá trị của bất kỳ kiểu dữ liệu nào:

Pair<int> intPair = new Pair<int>(10, 20);
Console.WriteLine($"First: {intPair.GetFirst()}, Second: {intPair.GetSecond()}");
Pair<string> stringPair = new Pair<string>("Hello", "World");
Console.WriteLine($"First: {stringPair.GetFirst()}, Second: {stringPair.GetSecond()}");

Trong ví dụ này, Pair<int>Pair<string> đều là các phiên bản của lớp generic Pair<T>, tương ứng với kiểu dữ liệu được chỉ định.

5. Các loại generics phổ biến trong C#

5.1. Generic Classes

Generic classes cho phép chúng ta định nghĩa một lớp mà hoạt động với một kiểu dữ liệu chưa biết trước. Ví dụ như List<T>, Dictionary<TKey, TValue>, và Nullable<T>.

public class MyGenericClass<T>
{
    private T genericField;
    public MyGenericClass(T value)
    {
        genericField = value;
    }
    public T GetValue()
    {
        return genericField;
    }
}

5.2. Generic Methods

Generic methods cho phép chúng ta định nghĩa một phương thức mà hoạt động với một hoặc nhiều kiểu dữ liệu chưa biết trước.

public class MyGenericClass
{
    public T MyGenericMethod<T>(T argument)
    {
        return argument;
    }
}

5.3. Generic Interfaces

Generic interfaces cho phép chúng ta định nghĩa một interface mà hoạt động với một kiểu dữ liệu chưa biết trước.

public interface IMyGenericInterface<T>
{
    T GetValue();
}

5.4. Constraints

Chúng ta có thể áp dụng ràng buộc (constraints) cho generics để hạn chế loại kiểu dữ liệu có thể được sử dụng trong một generic class hoặc method.

public class MyClass<T> where T : class
{
    // T phải là một tham chiếu kiểu dữ liệu (class)
}

5.5. Delegate Generics

Generics cũng có thể được sử dụng với delegate, cho phép chúng ta định nghĩa một delegate mà hoạt động với một kiểu dữ liệu chưa biết trước.

public delegate T MyGenericDelegate<T>(T arg);

6. Ưu điểm của Generics trong C#

  • Tái sử dụng mã: Generics giúp chúng ta viết mã một cách tổng quát, cho phép tái sử dụng mã cho nhiều kiểu dữ liệu khác nhau.
  • Tính linh hoạt: Cho phép chúng ta làm việc với bất kỳ kiểu dữ liệu nào mà không cần phải chuyển đổi kiểu.
  • Hiệu suất: Generics hỗ trợ kiểm tra kiểu tại thời gian biên dịch, giúp tránh được sự chậm trễ của kiểm tra kiểu tại thời gian chạy.

Kết luận

Generics là một tính năng mạnh mẽ trong C# giúp chúng ta viết mã linh hoạt và tái sử dụng. Bằng cách sử dụng Generics, chúng ta có thể tạo ra các thành phần phần mềm linh hoạt và dễ mở rộng, giúp tăng cường hiệu suất và sự linh hoạt của ứng dụng.


Stringee Communication APIs là giải pháp cung cấp các tính năng giao tiếp như gọi thoại, gọi video, tin nhắn chat, SMS hay tổng đài CSKH cho phép tích hợp trực tiếp vào ứng dụng/website của doanh nghiệp nhanh chóng. Nhờ đó giúp tiết kiệm đến 80% thời gian và chi phí cho doanh nghiệp bởi thông thường nếu tự phát triển các tính năng này có thể mất từ 1 - 3 năm.

Bộ API giao tiếp của Stringee hiện đang được tin dùng bởi các doanh nghiệp ở mọi quy mô, lĩnh vực ngành nghề như TPBank, VOVBacsi24, VNDirect, Shinhan Finance, Ahamove, Logivan, Homedy, Adavigo, bTaskee…

Quý bạn đọc quan tâm xin mời đăng ký NHẬN TƯ VẤN TẠI ĐÂY:

Banner bottom