Chia Sẻ Tìm hiểu về Generic trong C#

uocmo_kchodoi

Moderator
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.

Code.jpg

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)

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();

Generics ở 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.
Ví dụ:

Bạn tạo hai lớp có tên lần lượt là TestGeneric, TestCsharp như sau:

Lớp TestGeneric:

using System;
using System.Collections.Generic;
namespace VietJackCsharp
{
class TestGeneric <T>
{
private T[] array;
public TestGeneric(int size)
{
array = new T[size + 1];

}

public T getItem(int index)
{
return array[index];
}

public void setItem(int index, T value)
{
array[index] = value;
}
}
}
Lớp TestCsharp:

using System;
using System.Collections;
namespace VietJackCsharp
{
class TestCsharp
{
static void Main(string[] args)
{
Console.WriteLine("Vi du minh hoa Generic trong C#");
Console.WriteLine("-------------------------------------");

//khai bao mot mang cac so nguyen
TestGeneric<int> intArray = new TestGeneric<int>(5);
//thiet lap cac gia tri
for (int c = 0; c < 5; c++)
{
intArray.setItem(c, c * 5);
}

//lay va hien thi cac gia tri
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getItem(c) + " ");
}

Console.WriteLine();

//khai bao mot mang ky tu
TestGeneric<char> charArray = new TestGeneric<char>(5);

//thiet lap gia tri
for (int c = 0; c < 5; c++)
{
charArray.setItem(c, (char)(c + 97));
}

//lay va hien thi cac gia tri
for (int c = 0; c < 5; c++)
{
Console.Write(charArray.getItem(c) + " ");
}
Console.WriteLine();

Console.ReadKey();
}
}
}

Nếu bạn không
sử dụng lệnh Console.ReadKey(); thì chương trình sẽ chạy và kết thúc luôn (nhanh quá đến nỗi bạn không kịp nhìn kết quả). Lệnh này cho phép chúng ta nhìn kết quả một cách rõ ràng hơn.

Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:

generic.PNG

Nguồn: Sưu tầm
 
Sửa lần cuối bởi điều hành viên:
Các loại Generic

1 Generic Type Parameters
Trong ví dụ ở đầu bài viết thì ExampleList<T> không thực sự là một Generic Type Parameter, bởi vì muốn dùng nó chúng ta cần khởi tạo. Generic Type Parameter có thể hiểu nó như một kiểu dữ liệu có thể dùng khai báo biến, trong đó có một kiểu T là động, nghĩa là chúng ta có thể truyền nhiều kiểu dữ liệu khác nhau cho nó, ví dụ trong C# ta có những kiểu có thể dùng Generic Type Parameter như:

public int IComparer<T>() { return 0; }
public delegate bool Predicate<T>(T item);
public struct Nullable<T> where T : struct { /*...*/ }

Đây chúng ta có thể gọi là Generic Type

2. Generic classes
Generic classes đóng gói các xử lý mà không chỉ định rõ kiểu dữ liệu. Hầu hết các trường hợp phổ biến sử dụng generic classes là với collections giống như: danh sách liên kết (Linked List), hash tables, queues, trees,..Các xử lý giống như thêm mới, gỡ bỏ item trong collection cơ bản được thực hiện theo cùng một cơ chế bất kể kiểu dữ nào được lưu trữ trong collection.

Khi tạo custom generic classes của riêng bạn, những điểm quan trọng cần xem xét đó là:

  • Những types nào được khái quát hóa đến type parameters.
    Theo quy tắc, bạn càng tham số hóa càng nhiều, code của bạn càng mềm dẻo và dễ tái sử dụng. Tuy nhiên, điều đó cũng có thể làm code khó để đọc và hiểu cho người khác.
  • Những ràng buộc gì để áp dụng type parameters
  • Liệu implement một hoặc nhiều interfaces.
    Cho ví dụ, nếu bạn đang thiết kế một class mà sẽ sử dụng để tạo các items dựa trên collection, bạn có thể phải implement một interface giống như IComparable<T> where T
Code bên dưới là một ví dụ về Generic class:

class Demo<T>
{
T value;

public Demo(T t)
{
value = t;
}

public void Write()
{
Console.WriteLine(value);
}
}

class Program
{
static void Main()
{
Demo<int> test1 = new Demo<int>(10);
test1.Write();

Demo<string> test2 = new Demo<string>("Cat");
test2.Write();

Console.ReadLine();
}
}

3 Generic Interface
Nó thường hữa ích để định nghĩa cho collection classes, hoặc cho generic classes. Có thể lấy ví dụ như IComparable<T>.

Ví dụ sau demo cách sử dụng Generic interface. Giả sử ta xây dựng một chức năng getAll và save data của đối tượng Book và Author xuống DB. Chúng ta sẽ sử dụng một base interface và triển khai như bên dưới:

class Book
{
public string Name { get; set; }

public int Page { get; set; }
}

class Author
{
public string Name { get; set; }

public int Age { get; set; }
}

interface IBaseRepository<T>
{
List<T> getAll();

T Save(T item);
}

class BaseRepository<T>: IBaseRepository<T>
{
public List<T> getAll()
{
return new List<T>();
}

public T Save(T item)
{
return item;
}
}

class Program
{
static void Main()
{
BaseRepository<Book> bookRepository = new BaseRepository<Book>();
BaseRepository<Author> authorRepository = new BaseRepository<Author>();
Book book = bookRepository.Save(new Book { Name = "Book1", Page = 100 });
Author author = authorRepository.Save(new Author { Name = "Author1", Age = 50 });

Console.WriteLine("Book: {0} - page: {1}", book.Name, book.Page);
Console.WriteLine("Name: {0} - age: {1}", author.Name, author.Age);


Console.ReadLine();
}
}

4 Generic method
Một generic method được khai báo với type parameter, có thể hiểu là kiểu của tham số là dynamic (không được chỉ định trước cho đến khi gọi method đó) như code bện dưới:

class Program
{
static int Compare<T>(T first, T second)
{
if(first.Equals(second))
{
return 0;
}

return 1;
}

static void Main()
{
int result1 = Compare(2, 2);
int result2 = Compare("abc", "def");

Console.WriteLine(result1);
Console.WriteLine(result2);

Console.ReadLine();
}
 

Chủ đề mới

VnKienthuc lúc này

Không có thành viên trực tuyến.

Định hướng

Diễn đàn VnKienthuc.com là nơi thảo luận và chia sẻ về mọi kiến thức hữu ích trong học tập và cuộc sống, khởi nghiệp, kinh doanh,...
Top