Series C# hay ho: Generic là cái thứ chi chi

Generic là một vị anh hùng thầm lặng trong C#.NET (Dân gian còn gọi là anh hùng núp). Generic 1 trong “5 anh em siêu nhân” cấu thành LINQ (4 người còn lại là: Extension method, Delegate, Lambda Expression và yield). Anh núp trong 50% những dòng code chúng ta viết, đến nỗi chúng ta dùng 1 cách vô thức, không biết đến sự tồn tại hay tên gọi của anh.

pr-2

Mình viết bài này nhằm vinh danh “anh hùng thầm lặng” Generic, cũng như hướng dẫn các bạn cách tạo, sử dụng generic class và generic method.

1. Sự ra đời của Generic

Rất nhiều người trong chúng ta sử dụng generic mà không biết rằng nó là generic. Ví dụ, khi muốn tạo 1 danh sách các học sinh, ta thường viết:

//Một list chứa các object là Student
List<Student> students = new List<Student>();
students.Add(new Student()); //Code đúng
students.Add(new Car()); //Compile lỗi
//Lấy học sinh đầu tiên.
//Compiler tự hiểu kết quả là Student
Student first = students.First();

Có bạn sẽ hỏi: Ơ, code này bình thường mà, có thấy cái thằng anh hùng núp Generics đâu? Vâng, Generics núp trong 2 dấu ngoặc nhọn đấy bạn <>.

Để dễ hiểu, ta hãy quay lại thời .NET 1.0, khi generic chưa xuất hiện:

//Không có generic
//Một list chứa các object
List students = new List();
students.Add(new Student()); //Compile bình thường
students.Add(new Car()); //Compile bình thường
//Lấy object đầu tiên, phải ép kiểu sang Student
Student first = (Student)students.First();

Không có generic, compiler không thể check lỗi lúc compiler. Do đó, ở dòng 2, ta có thể thêm 1 object Car và list gồm các object Student. Khi lấy 1 phần tử ra, ta cũng phải ép kiểu, vì compiler chỉ hiểu nó là 1 object. Vì những lý do đó, generic đã được thêm vào ở .NET 2.0. Tác dụng của generic:

  • Giúp tái sử dụng code. Ví dụ: Ta chỉ cần viết class List<T>, T ở đây có thể là bất kì class gì.
  • Hỗ trợ compiler bắt lỗi trong quá trình compiler (Hạn chế được tình trạng như dòng 2).
  • Không còn phải ép kiểu từ object.
  • …..

2. Sử dụng Generic 

Generic được ứng nhiều trong các class List, Dictionary và LINQ,… Khi nào chúng ta nên dùng LINQ? Khi chúng ta cần viết 1 hàm có thể tái sử dụng cho nhiều kiểu dữ liệu.

Nghe hơi khó hiểu phải không? Giả sử mình muốn bạn viết 1 hàm swap cho 2 biến, có thể sử dụng cho các kiểu int, double, bool, kể cả class thì bạn sẽ viết thế nào?

LetSwap_1

Nếu không có generic

public void swap (int ref a, int ref b);
public void swap (double ref a, double ref b);
public void swap (bool ref a, bool ref b);
public void swap (object ref a, object ref b);
.........

//Nếu muốn swap 2 student, phải viết thêm hàm
public void swap (Student ref a, Student ref b);

Với sức mạnh của Generic, ta chỉ cần viết 1 và chỉ 1 method duy nhất như sau:

//Dấu <T> để biết method là generic method
public static void Swap<T>(ref T a, ref T b)
{
  T temp = b;
  b = a;
  a = temp;
}

Sử dụng generic như sau:

 int firstInt = 1;
 int secondInt = 2;
 Swap<int>(ref firstInt, ref secondInt);

 double firstDouble = 1.5;
 double secondDouble = 2.5;
 //Có thể bỏ qua dấu <> vì compiler tự hiểu kiểu double
 //Dựa trên tham số ta truyền vào
 Swap(ref firstDouble, ref secondDouble);

Các bạn có thể chạy thử code ở đây: https://dotnetfiddle.net/7zv1lM. Dotnet fiddle cho phép ta viết và chạy code C# online, không cần cài đặt gì nhé.

Một ví dụ nữa, mình vừa gặp trong quá trình code, đó là sử dụng để lấy một giá trị ngẫu nhiên trong list. Ta viết 1 method generic và sử dụng (Method này có thể sử dụng với kiểu int, double, … cả Student hay object, …).

 public static T getRandomElement<T>(List<T> list)
{
   Random ran = new Random();
   int randomIndex = ran.Next(list.Count - 1);
   return list[randomIndex];
}

//Sử dụng
List<int> intList = new List<int> {1,2,3,4,5,6 };
List<double> doubleList = new List<double> {0.4, 0.6,0.8,4.5,0.2 };

var randomInt = getRandomElement(intList);
var randomDouble = getRandomElement(doubleList);

Cải tiến 1 chút bằng cách biến method này thành extension method cho class List

public static class ListExt
{
  public static T randomElement<T>(this List<T> list)
  {
    Random ran = new Random();
    int randomIndex = ran.Next(list.Count - 1);
    return list[randomIndex];
  }
} 

//Sử dụng
List<int> intList = new List<int> {1,2,3,4,5,6 };
List<double> doubleList = new List<double> {0.4, 0.6,0.8,4.5,0.2 };
//Extension method
Console.WriteLine(intList.randomElement());
Console.WriteLine(doubleList.randomElement());

Các bạn có thể test thử đoạn code này tại đây: https://dotnetfiddle.net/MZJ5HY

Như đã giới thiệu ở đầu bài viết, Generic là 1 công cụ khá mạnh, có thể tiết kiệm thời gian code, cũng như giảm thiểu khá nhiều bug trong code của bạn. Nếu muốn tìm hiểu thêm, các bạn có thể đọc thêm ở đây: https://msdn.microsoft.com/en-us/library/512aeb7t.aspx. Hẹn gặp lại ở bài viết sau.

5 thoughts on “Series C# hay ho: Generic là cái thứ chi chi”

  1. hay ghê!
    Có những thứ tưởng rất chi là bình thường và hiển nhiên như cái mà nó thật bất thường và thì thoảng gặp generic không hiểu là cái chi chi. Thank a! 😀

    Like

Leave a comment