Phân cấp lớp trong C#

Tính kế thừa (Inheritance) là một trong bốn trụ cột quan trọng của lập trình hướng đối tượng (OOP).

  • Tính đóng gói (Encapsulation).
  • Tính kế thừa (Inheritance).
  • Tính đa hình (Polymorphirsm).
  • Tính trừu tượng (Abstraction).

Nếu chưa hiểu rõ về lập trình hướng đối tượng thì bạn nên tham khảo bài viết dưới đây trước:

Lập trình hướng đối tượng (OOP) trong C# | Comdy
Lập trình hướng đối tượng (OOP) trong C# là gì? 5 khái niệm và 4 tính chất quan trọng của lập trình hướng đối tượng là gì?

Tính kế thừa là khả năng tạo một lớp mới dựa trên một lớp có sẵn. Lớp có sẵn là lớp cha, lớp mới là lớp con và lớp con thừa kế các thành phần được định nghĩa ở lớp cha.

Hãy xem ví dụ về hệ thống phân cấp lớp sau đây:

public class Small
{ 

}
public class Big: Small
{

}
public class Bigger : Big
{ 
    
}

Theo ví dụ trên, Small là lớp cơ sở cho lớp Big và lớp Big là lớp cơ sở cho lớp Bigger. Điểm cần nhớ ở đây là một lớp dẫn xuất sẽ luôn có một cái gì đó nhiều hơn một lớp cơ sở, vì vậy lớp cơ sở tương đối nhỏ hơn lớp dẫn xuất.

Bây giờ, hãy xem xét việc khởi tạo sau:

Khởi tạo class trong hệ thống phân cấp lớp trong C#

Như bạn có thể thấy ở trên, một lớp cơ sở có thể chứa một lớp dẫn xuất nhưng một lớp dẫn xuất không thể chứa một lớp cơ sở.

Nói cách khác, có thể tạo thể hiện của lớp con và gán cho một biến của lớp cha nhưng không thể tạo thể hiện của lớp cha và gán cho một biến của lớp con.

Bây giờ, chúng ta hãy tìm hiểu về hiệp phương sai và chống chỉ định trong C#.

Hiệp phương sai trong C#

Hiệp phương sai (Covariance) cho phép bạn truyền một kiểu dẫn xuất cho một phương thức yêu cầu một kiểu cơ sở.

Về cơ bản, lớp dẫn xuất là lớp cơ sở được bổ sung thêm các chức năng. Vì vậy, hiệp phương sai cho phép bạn sử dụng một lớp dẫn xuất để thay cho một lớp cơ sở.

Hiệp phương sai có thể được áp dụng trên delegate, generic, array, interface, v.v.

Small small1 = new Big();
Small small2 = new Bigger();
Big big = new Bigger();

object[] objs = new string[10];

Hiệp phương sai với delegate

Nếu bạn chưa đọc bài hướng dẫn về delegate thì có thể tham khảo tại đây trước khi tiếp tục nhé:

Delegate trong C# | Comdy
Delegate trong C# là gì? Delegate trong C# dùng để làm gì? Cách sử dụng Delegate trong C#.

Hiệp phương sai với delegate cho phép linh hoạt trong kiểu trả về của các phương thức delegate. Hãy xem ví dụ sau:

public delegate Small CovarDel(Big mc);

class Program
{
    static Big Method1(Big bg)
    {
        Console.WriteLine("Method1");
    
        return new Big();
    }
    
    static Small Method2(Big bg)
    {
        Console.WriteLine("Method2");
    
        return new Small();
    }
        
    static void Main(string[] args)
    {
        CovarDel del = Method1;

        Small sm1 = del(new Big());

        del = Method2;
        Small sm1 = del(new Big());
    }
}

Kết quả khi biên dịch và thực thi chương trình như sau:

Method1
Method2

Như bạn có thể thấy trong ví dụ trên, delegate mong đợi một kiểu trả về là Small (lớp cơ sở) nhưng chúng ta vẫn có thể gán Method1 trả về kiểu Big (lớp dẫn xuất) và Method2 có chữ ký giống như mong đợi của delegate.

Do đó, hiệp phương sai cho phép bạn gán một phương thức cho delegate có kiểu trả về ít dẫn xuất hơn.

Chống chỉ định trong C#

Chống chỉ định (Contravariance) được áp dụng cho các tham số. Chống chỉ định cho phép một phương thức với tham số của lớp cơ sở được gán cho một delegate yêu cầu ​​tham số của lớp dẫn xuất.

Tiếp tục với ví dụ trên, thêm phương thức Method3 có kiểu tham số khác với delegate như sau:

delegate Small CovarDel(Big mc);

class Program
{
    static Big Method1(Big bg)
    {
        Console.WriteLine("Method1");
        return new Big();
    }
    
    static Small Method2(Big bg)
    {
        Console.WriteLine("Method2");
        return new Small();
    }

    static Small Method3(Small sml)
    {
        Console.WriteLine("Method3");
        
        return new Small();
    }
    
    static void Main(string[] args)
    {
        CovarDel del = Method1;
        del += Method2;
        del += Method3;

        Small sm = del(new Big());
     }
}

Kết quả khi biên dịch và chạy chương trình:

Method1
Method2
Method3

Như bạn có thể thấy, Method3 có một tham số của lớp Small trong khi delegate yêu cầu một tham số của lớp Big. Tuy nhiên, bạn có thể sử dụng Method3 với delegate.

Bạn cũng có thể sử dụng hiệp phương sai và chống chỉ định trong cùng một phương thức như dưới đây.

delegate Small CovarDel(Big mc);

class Program
{
    static Big Method4(Small sml)
    {
        Console.WriteLine("Method4");
    
        return new Big();
    }

    static void Main(string[] args)
    {
        CovarDel del = Method4;
    
        Small sm = del(new Big());
    }
}

Kết quả khi biên dịch và chạy chương trình:

Method4

Có bạn nào bị nhức đầu, chóng mặt khi đọc tới đây không? Hãy để lại comment bên dưới cho mọi người biết nhé.

Lập Trình C#Lập Trình C# Cơ Bản
Bài Viết Liên Quan:
int[] và int[,] trong C#: Ai nhanh hơn
Trung Nguyen 10/10/2020
int[] và int[,] trong C#: Ai nhanh hơn

Hiểu được sự khác biệt giữa các loại mảng trong C# sẽ giúp bạn chọn cấu trúc dữ liệu chính xác cho mọi trường hợp.

Struct và class trong C#: Ai nhanh hơn
Trung Nguyen 09/10/2020
Struct và class trong C#: Ai nhanh hơn

Trong bài viết này, tôi sẽ so sánh sự khác biệt về hiệu suất giữa struct và class trong C#: Ai nhanh hơn.

Best practice cho performance trong C#
Trung Nguyen 03/10/2020
Best practice cho performance trong C#

Mục tiêu của bài viết này là cung cấp một danh sách không đầy đủ các code mẫu cần tránh, vì chúng rủi ro hoặc performance kém.

Đọc ghi file (File I/O) trong C#
Trung Nguyen 26/04/2020
Đọc ghi file (File I/O) trong C#

Hướng dẫn này sẽ giúp bạn tìm hiểu về đọc ghi file (File I/O) trong C# và sử dụng các lớp tiện ích để đọc ghi file.