Bài 5: Tính thừa kế và Đa hình - Nền tảng lập trình C#


Thừa kế trong lập trình hướng đối tượng cho phép bạn có thể tạo ra 1 lớp mới có đầy đủ tất cả những tính chất và phương thức của 1 lớp đã có mà không phải viết lại bất cứ 1 dòng lệnh nào


  • Tính thừa kế (Inheritance)
  • Lớp trừu tượng (Abstract)
  • Lớp niêm phong (Sealed)
  • Tính đa hình của lớp (Polymorphism)

Tính thừa kế (Inheritance)

Khái niệm

  • Tính thừa kế là một khái niệm nền tảng cho phép tái sử dụng mã lệnh đang tồn tại và điều này giúp tiết kiệm được thời gian trong việc lập trình
  • Các class có thể thừa kế từ class khác. Class mới được gọi là class được dẫn xuất (hay còn gọi là class con) sẽ được quyền truy xuất đến tất cả các thành viên dữ liệu và các phương thức không được biểu thị private của class cơ sở (hay còn gọi là class cha)

Xây dựng lớp kế thừa

Cú pháp: Tên_class_con : Tên_class_cơ_sở

public class ParentClass
{
    public ParentClass(){
        Console.WriteLine("Phuong thuc khoi tao cua class cha.");
    }

    public void print(){
        Console.WriteLine("Phuong thuc print cua class cha.");
    }
}
public class ChildClass : ParentClass
{
    public ChildClass(){
        Console.WriteLine("Phuong thuc khoi tao cua class con");
    }

    public static void Main(){
        ChildClass child = new ChildClass();
        child.print();
        Console.Read();
    }
}

ví dụ tính kế thừa

class cha
{
    public cha(){
        Console.WriteLine("cha: Khong tham so");
    }
    public cha(int a){
        Console.WriteLine("cha: Tham so int");
    }
}
class con_gai : cha
{
    public con_gai(int a){
        Console.WriteLine("Con gai: Tham so int");
    }
}
class con_trai : cha
{
    public con_trai(int a):base(a){
        Console.WriteLine("Con trai: Tham so int");
    }
}
class Program
{
    static void Main(string[] args){
        con_gai Hanh = new con_gai(1);
        con_trai Nam = new con_trai(1);
        Console.Read();
    }
}

ví dụ thừa kế 2

Lớp trừu tượng (Abstract)

Khái niệm

  • class trừu tượng thực chất là class cơ sở (base class) mà các class khác có thể được dẫn xuất từ nó
  • Các class không phải là class trừu tượng (non-abstract class) được gọi là lớp cụ thể (concrete class)
  • class trừu tượng có thể có cả hai loại phương thức: phương thức trừu tượng và phương thức cụ thể
  • Một kiểu được dẫn xuất từ một lớp cơ sở trừu tượng thừa kế tất cả các thành viên kiểu cơ sở bao gồm sự thực thi mọi phương thức
  • Khi nào thì sử dụng class trừu tượng?
    • Nếu muốn tạo các class mà các class này sẽ chỉ là các class cơ sở, và không muốn bất cứ ai tạo các đối tượng của các kiểu class này.
    • Class trừu tượng thường được dùng để biểu thị rằng nó là class không đầy đủ và rằng nó được dự định sẽ chỉ được dùng như là một class cơ sở.

Cú pháp: 

<khai báo cấp độ truy xuất> abstract class tên_class
{
// Các thành viên của class trừu tượng.
}

Ví dụ:

class Program
{
    abstract class MyAbs
    {
        public void NonAbMethod(){
            Console.WriteLine("Phuong thuc nay khong phai phuong thuc truu tuong");
        }
    }
    class MyClass : MyAbs { }
    static void Main(string[] args){
        //MyAbs mb = new MyAbs(); //Không thể tạo một instance của class trừu tượng
        MyClass mc = new MyClass();
        mc.NonAbMethod();//Gọi phương thức không phải phương thức trừu tượng
        Console.Read();
    }
}

ví dụ phương thức trừu tượng

Ví dụ:

class Program
{
    abstract class MyAbs
    {
        public void NonAbMethod(){
            Console.WriteLine("Phuong thuc cu the (Non-Abstract Method)");
        }
        public abstract void AbMethod();
    }
    //Phải cài đặt dựa trên các phương thức class trừu tượng
    class MyClass : MyAbs 
    {
        public override void AbMethod(){
            Console.WriteLine("Phuong thuc truu tuong (Abstract method)");
        }
    }
    static void Main(string[] args){
        MyClass mc = new MyClass();
        mc.NonAbMethod();
        mc.AbMethod();
        Console.Read();
    }
}

ví dụ phương thức trừu tượng 2

Chú ý Xây dựng lớp trừu tượng

  • Mục tiêu của một class trừu tượng là cung cấp định nghĩa chung của một class cơ sở mà nhiều class được dẫn xuất có thể chia sẻ.
  • Một thành viên abstract không thể là static
  • Không thể tạo một instance của class trừu tượng
  • Một class trừu tượng không thể được niêm phong (Sealed).
  • Một phương thức trừu tượng không thể là private
  • Từ khóa Override : hành động ghi đè (Overriding) là hành động sửa đổi hoặc thay thế sự cài đặt của class cha với một cài đặt mới. Các thành viên virtual hoặc abstract của class cha cho phép các class dẫn xuất ghi đè chúng.
  • Phương thức abstract thực chất là một phương thức virtual ngầm định.

Lớp niêm phong (Sealed)

Khái niệm

  • Từ khóa sealed được sử dụng để biểu thị khi khai báo một class nhằm ngăn ngừa sự dẫn xuất từ một class, điều này cũng giống như việc ngăn cấm một class nào đó có class con.
  • Một class sealed cũng không thể là một class trừu tượng.
  • Các structs trong C# được ngầm định sealed. Do vậy, chúng không thể được thừa kế

Cú pháp:

<khai báo cấp độ truy xuất> sealed class tên_class
{
// Các thành viên của class trừu tượng.
}

Ví dụ:

class Program
{
    sealed class Class_sealed{
        public int x;
        public int y;
    }
    //khai báo dẫn xuất này không hợp lệ
    //class MyClass : Class_sealed
    //{
    //}
    static void Main(string[] args){
        Class_sealed test = new Class_sealed();
        test.x = 20;
        test.y = 17;
        Console.WriteLine("x = {0}, y = {1}", test.x, test.y);
        Console.Read();
    }
}

Tính đa hình của lớp (Polymorphism)

  • Từ khóa base, this
  • Ghi đè (overriding)
  • Nạp chồng hàm (Overloading)

Từ khóa base: Được sử dụng để tham chiếu đến lớp cơ sở từ lớp dẫn xuất. Ví dụ: xét lớp cơ sở nhan_vien với khai báo như sau:

public class nhan_vien
{
    string ho_ten;
    int tuoi;
    //Phương thức khởi tạo không có tham số
    public nhan_vien(){
        ho_ten = "";
        tuoi = 0;
    }
    //Phương thức khởi tạo không có tham số
    public nhan_vien(string pho_ten, int ptuoi){
        ho_ten = pho_ten;
        tuoi = ptuoi;
    }
}

Ví dụ: xét lớp dẫn xuất nv_van_phong với khai báo như sau:

public class nv_van_phong : nhan_vien
{
    int so_ngay_vang;
    public nv_van_phong(string photen,int ptuoi,int pngayvang):base(photen,ptuoi){
        so_ngay_vang = pngayvang;
    }
}

Trong ví dụ trên, để tham chiếu đến phương thức khởi tạo có tham số trong lớp cơ sở nhan_vien phải sử dụng từ khóa base

Từ khóa this: Được sử dụng để tham chiếu đến lớp hiện hành (lớp chứa đoạn lệnh đang cài đặt). Ví dụ: xét lớp cơ sở nhan_vien với khai báo như sau:

public class nhan_vien
{
    string ho_ten;
    int tuoi;
    //Phương thức khởi tạo không có tham số
    public nhan_vien(){
        ho_ten = "";
        tuoi = 0;
    }
    //Phương thức khởi tạo không có tham số
    public nhan_vien(string pho_ten, int ptuoi){
        this.ho_ten = pho_ten;
        this.tuoi = ptuoi;
    }
}

Ghi đè (overriding)

  • Khái niệm ghi đè (overriding) được dùng để định nghĩa lại phương thức của lớp cơ sở (lớp cha) trong lớp dẫn xuất (lớp con kế thừa)
  • Các điểm cần lưu ý khi thực hiện ghi đè:
    • Phương thức ở lớp cơ sở và lớp dẫn xuất phải có cùng dạng hàm (signature) và kiểu dữ liệu trả về
    • Phương thức lớp cơ sở phải được khai báo với từ khóa virtual
    • Phương thức lớp dẫn xuất phải được khai báo với từ khóa override

Ví dụ: xét lớp cơ sở nhan_vien với khai báo:

public class nhan_vien
{
    string ho_ten;
    int tuoi;
    float he_so;
    float luong_co_ban = 400000;
    //Phương thức khởi tạo không có tham số
    public nhan_vien(){
        ho_ten = "";
        tuoi = 0;
    }
    //Phương thức khởi tạo không có tham số
    public nhan_vien(string pho_ten, int ptuoi,float phe_so){
        this.ho_ten = pho_ten;
        this.tuoi = ptuoi;
        this.he_so = phe_so;
    }
    public virtual double tinh_luong(){
        return luong_co_ban * he_so;
    }
}

Ví dụ: xét lớp dẫn xuất với khai báo:

public class nv_van_phong : nhan_vien
{
    int so_ngay_vang;
    float don_gia_phat = 50000;
    public nv_van_phong(string pho_ten, int ptuoi, int phe_so, int pngay_vang): base(pho_ten, ptuoi, phe_so){
        so_ngay_vang = pngay_vang;
    }
    public override double tinh_luong(){
        return base.tinh_luong() - so_ngay_vang * don_gia_phat;
    }
}

Với ví dụ trên, phương thức tinh_luong trong lớp dẫn xuất được ghi đè lên phương thức tinh_luong trong lớp cơ sở

Lưu ý Ghi đè (overriding)

  • Các phương thức ghi đè phải trùng tên
  • Không thể ghi đè các phương thức tĩnh (không có từ khóa virtual)
  • Phương thức, thuộc tính, chỉ mục, sự kiện đều có thể được ghi đè bằng từ khóa virtual và override

Tính đa hình của lớp (Polymorphism)

  • Cho phép khai báo các phương thức trùng tên nhau nhưng có tham số khác nhau
  • Các điểm cần lưu ý khi thực hiện nạp chồng hàm:
    • Tên của các phương thức phải trùng nhau
    • Số lượng tham số phải khác nhau
    • Kiểu dữ liệu của các tham số và thứ tự các tham số phải khác nhau

Ví dụ: xét lớp nhan_vien với khai báo sau:

ví dụ nạp chồng hàm

Nhận xét Nạp chồng hàm (Overloading)

  • Trong lớp nhan_vien, có hai phương thức khởi tạo trùng tên (cùng là nhan_vien) nhưng có số lượng tham số khác nhau. Ta gọi hai phương thức khởi tạo này được nạp chồng hàm (Overriding)
  • Tất cả các phương thức trong C# đều có thể nạp chồng hàm

Chia sẽ bài viết :


Bài viết liên quan