Trong bài này chúng ta sẽ tìm hiểu:

  • Khái niệm generic
  • Các đặc trưng của generic
  • Lớp generic (generic class)
  • Phương thức generic (generic method)
  • Generic delegate
  • Giao diện generic (generic interface)

Generic là gì

Trở ngại lớn nhất của collection là nó không quan tâm đến kiểu dữ liệu, nghĩa là ta có thể thêm bất kì đối tượng nào vào collection. .NET Framework 2.0 giới thiệu một khái niệm mới gọi là generic, giúp khắc phục điểm yếu và kế thừa những điểm mạnh của collection. Generic cho phép chúng ta viết một lớp hay phương thức có thể làm việc với bất kỳ dữ liệu nào.

Chúng ta  có thể viết đặc tả cho một lớp hay phương thức và sử dụng các tham số để thay thế các kiểu cụ thể – gọi là các tham số kiểu (type parameters). Khi trình biên dịch bắt gặp phương thức khởi tạo của lớp hay hàm gọi phương thức, nó sẽ phát sinh mã để xử lý theo kiểu dữ liệu cụ thể. Để dùng generic, chúng ta phải khai báo namespace System.Collections.Generic. Với việc sử dụng generic, chúng ta sẽ không phải lo lắng về những rủi ro tiềm tàng khi ép kiểu hay boxing.

Một vài đặc trưng cơ bản của generic

– Generic tối đa hoá khả năng sử dụng lại các đoạn mã, an toàn kiểu và hiệu năng của ứng dụng.

– Generic được dùng phổ biến để tạo các lớp collection.

– Namespace System.Collections.Generic chứa nhiều lớp generic mới, có thể được dùng để thay thế các lớp collection, như ArrayList, trong namespace System.Collection.

– Chúng ta có thể tạo ra các lớp generic, giao diện generic, phương thức generic, sự kiện generic hay delegate generic cho riêng mình.

– Các lớp generic có thể bị ràng buộc cho phép truy cập đến các phương thức dựa theo các kiểu dữ liệu cụ thể.

Lớp generic

Generic có thể được khai báo như lớp – gọi là lớp generic. Ví dụ sau khai báo một lớp generic có tham số kiểu T và cách sử dụng lớp generic:


// khai báo lớp generic.

public class GenericList<T>

{

   void Add(T input) { }

}

class TestGenericList

{

   private class ExampleClass { }

   static void Main()

   {

       // khai báo một danh sách chứa các phần tử kiểu int.

       GenericList<int> list1 = new GenericList<int>();

       // khai báo một danh sách chứa các phần tử kiểu string.

       GenericList<string> list2 = new GenericList<string>();

       // khai báo một danh sách chứa các phần tử kiểu ExampleClass.

       GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();

   }

}

Phương thức generic

Ở trên, chúng ta đã đưa ra một ví dụ về lớp generic. Generic cũng có thể được khai báo theo kiểu phương thức như ví  dụ sau:


class Program {

    // phương thức generic

    static void Swap<T>(ref T lhs, ref T rhs) {

        T temp;

        temp = lhs;

        lhs = rhs;

        rhs = temp;

    }

   static void Main(string[] args) {

     int a, b;

     char c, d;

     a = 10;

     b = 20;

     c = 'I';

     d = 'V';

    //gọi swap

    Swap<int>(ref a, ref b);

    Swap<char>(ref c, ref d);

  }

}

Generic delegate

Generic cũng có thể  định nghĩa như delegate. Ví dụ sau sẽ định nghĩa và sử dụng generic delegate:


using System;

using System.Collections.Generic;

// định nghĩa generic delegate

delegate T NumberChanger<T>(T n);

namespace GenericDelegateAppl {

   class TestDelegate {

       static int num = 10;

       public static int AddNum(int p) {

          num += p;

          return num;

       }

       public static int MultNum(int q) {

         num *= q;

         return num;

       }

      public static int getNum() {

        return num;

      }

      static void Main(string[] args) {

         //tạo thể hiện delegate

         NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);

         NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);

         //gọi các phương thức thông qua đối tượng delegate

         nc1(25);

         nc2(5);

     }

   }

}

Giao diện generic

Generic có thể khai báo như là giao diện. Ví dụ sau khai báo giao diện generic:


// giao diện generic

interface IWrapper<T>

{

   void SetData(T data);

   T GetData();

}

// lớp generic kế thừa giao diện generic

class Wrapper<T> : IWrapper<T>

{

   private T storedData;

   void IWrapper<T>.SetData(T data)

   {

     this.storedData = data;

   }

   T IWrapper<T>.GetData()

   {

     return this.storedData;

   }

}

Chúng ta có thể dụng lớp generic Wrapper<T> như sau:


Wrapper<string> stringWrapper = new Wrapper<string>();

IWrapper<string> storedStringWrapper = stringWrapper;

storedStringWrapper.SetData("Hello");

Console.WriteLine("Stored value is {0}", storedStringWrapper.GetData());

Bây giờ nếu chúng ta thực hiện lệnh sau:


IWrapper<object> storedObjectWrapper = stringWrapper;

Khi thực thi, trình biên dịch sẽ báo lỗi:

Cannot implicitly  convert type ‘Wrapper<string>’ to ‘IWrapper<object>’

Chúng ta có thể thực hiện ép kiểu như sau:


IWrapper<object> storedObjectWrapper = (IWrapper<object>)stringWrapper;

Khi biên dịch sẽ phát sinh ngoại lệ InvalidCastException

Không thể gán một đối tượng IWrapper<A> đến một tham chiếu của kiểu IWrapper<B>, ngay cả kiểu A được thừa kế từ B. C# thực hiện điều này để đảm bảo an toàn kiểu cho đoạn mã chúng ta.

Học C# và WPF >