[ Đăng nhập | Ghi danh ]

Giới thiệu về kiểu Generic trong C#

Bài viết được cập nhật từ 7:17:15 SA, 07 Tháng Sáu 2012 bởi anbinhtrong

Bài viết này nhằm giới thiệu về cách lập trình Generic trong C#.

Giới thiệu về Generic Type

Generic là 1 tính năng mới trong .NET Framework 2.0 và và được tích hợp sâu trong Common Language Runtime (CLR).  Trong .NET Framework, Generic được giới thiệu là khái niệm về các kiểu tham số và được dùng để thiết kế class và phương thức nhằm để trì hoãn chỉ ra kiểu dữ liệu cho đến khi lớp hoặc phương thức được khai báo hoặc khởi tạo.

Ví dụ, bằng cách sử dụng tham số T là tham số chung, bạn có thể tạo 1 class duy nhất mà khi tham chiếu tới, bạn sẽ không gặp bất kỳ lỗi nào xảy ra trong lúc runtime khi ép kiểu hoặc boxing (chuyển giá trị từ value type sang reference type)

Tại sao phải sử dụng kiểu Generic

Một trong những điểm nổi bật của kiểu Generic là cho phép bạn kiểm tra cú pháp trong lúc biên dịch. Bạn có thể sử dụng nhiều kiểu dữ liệu khác nhau với cùng 1 đoạn code (tương tự như khái niệm Template trong C++)

Giả sử đầu tiên bạn thiết kế stack để chứa dữ liệu kiểu int:


public class IntStack
    {
        readonly int[] _dataArray = new int[100];
        int _currentPos;
        public void Push(int value)
        {
            _dataArray[_currentPos++] = value;
        }
        public int Pop()
        {
            return _dataArray[--_currentPos];
        }
    } 
 

 

Stack cho phép bạn lưu trữ và nhận dữ liệu theo kiểu LIFO (Last In First Out). Phương thức Push() dùng để đưa dữ liệu vào stack. Phương thức Pop() dùng để nhận dữ liệu từ Stack. Bạn có thể tham khảo thêm ở wikipedia


/// <summary>
        /// Example Int Stack
        /// </summary>
        static void Example1()
        {
            // create a new IntStack 
            var stack = new IntStack();
            // push some values into the stack 
            stack.Push(2);
            stack.Push(4);
            stack.Push(8);
            // pop values from the stack 
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("Pop value: {0}", stack.Pop());
            } 
        }
 

 

Và nếu vào một ngày đẹp trời nào đó, bạn muốn stack lưu trữ kiểu string (stack kiểu int vẫn sử dụng). Thay vì copy và sửa lại, bạn nhận thấy rằng, mọi kiểu dữ liệu được dẫn xuất từ System.Object. Bạn vui mừng và sửa lại:


public class ObjectStack
    {
        readonly object[] _dataArray = new object[10];
        int _currentPos;
        public void Push(object value)
        {
            _dataArray[_currentPos++] = value;
        }
        public object Pop()
        {
            return _dataArray[--_currentPos];
        }
    }
 

 Vấn đề đã được giải quyết, nhưng lại nảy sinh 1 vấn đề khác. Chúng ta muốn stack lưu trữ dữ liệu với kiểu dữ liệu được chỉ ra, nhưng với kiểu object, stack nhận bất kỳ kiểu dữ liệu.


/// <summary>
        /// Object Stack
        /// </summary>
        static void Example2()
        {
            var stack = new ObjectStack();
            stack.Push("2");
            stack.Push(4);
            stack.Push(8);
            // pop values from the stack 
            for (int i = 0; i < 3; i++)
            {
                var a = (int)stack.Pop();
                Console.WriteLine("Pop value: {0}", a);
            } 
        }
 

 

Vấn đề là nếu ai đó muốn xài stack kiểu int nhưng lại nhập vào chữ “2”, trình biên dịch sẽ không báo lỗi cho đến lúc runtime

Để khắc phục được điều này, chúng ta sử dụng kiểu Generic.

Sử dụng Generic Type

Đối với 1 class, bạn khai báo như sau:

Với T là kiểu bạn truyền vào cho class.

Tương tự với methods, interface, chúng ta khai báo như sau:


public class GenericMethods
    {
        public void DoSomething<T, TV>(T a, TV b)
        {
            //Do something
        }
    }

    public interface ISessionChannel<TSession> { /*...*/ }
 

 

Bạn có thể tham khảo chi tiết tại trang MSDN

Ví dụ tạo 1 Generic Stack

Cách dễ nhất để tạo 1 generic class là bạn hãy tạo 1 class non-generic trước, và sau đó hãy chuyển sang generic class sau.

Sau đây tôi sẽ ví dụ chuyển bài Stack từ kiểu int sang kiểu T:

public class GenericStack{} -> public class GenericStack<T>{}

Và bạn thay chỗ nào là kiểu “int” thành kiểu “T”


class GenericStack<T> 
    {
        readonly T[] _dataArray = new T[10]; 
        int _currentPos; 
        public void Push(T value) {
            _dataArray[_currentPos++] = value;
        }
        public T Pop()
        {
            return _dataArray[--_currentPos];
        }
        public T this[int index]
        {
            get
            {
                return _dataArray[index];
            }
        }
    }
 

 

Sử dụng GenericStack:


/// <summary>
        /// Generic Stack
        /// </summary>
        static void Example3()
        {
            var stack = new GenericStack<int>();
            stack.Push(2);
            stack.Push(4);
            stack.Push(8);
            // pop values from the stack 
            for (int i = 0; i < 3; i++)
            {
                var a = stack.Pop();
                Console.WriteLine("Pop value: {0}", a);
            }
        }
 

 

Lưu ý: Có một số trường hợp khi thay từ kiểu cụ thể sang kiểu T sẽ bị lỗi. (Kiểu T không hỗ trợ phép toán +, -, *, /…; hoặc 1 số hàm trên kiểu dữ liệu cụ thể).

Kết luận

Trong bài viết này, tôi đã giới thiệu cho bạn cách dùng kiểu Generic và xây dựng 1 class GenericStack đơn giản. Hi vọng qua ví dụ đó, giúp bạn phần nào nắm được ý nghĩa và tầm quan trọng của kiểu Generic trong lập trình. Chúc các bạn thành công.

Update comment..