Bài 1: Giới thiệu về Trình quản lý quảng cáo

Tính Đa Hình (Polymorphism) trong C#

1. Tính đa hình là gì?

Tính đa hình là khả năng một phương thức có thể hoạt động khác nhau tùy thuộc vào đối tượng cụ thể. Trong C#, có 2 loại đa hình chính:

  1. Đa hình thời gian biên dịch (Compile-time polymorphism) – Thể hiện qua method overloading.
  2. Đa hình thời gian chạy (Runtime polymorphism) – Thể hiện qua method overriding, interface, và lớp trừu tượng (abstract class).

2. Đa hình thời gian biên dịch (Method Overloading)

  • Cùng một phương thức nhưng có thể có nhiều phiên bản với tham số khác nhau.
  • Việc gọi phương thức nào sẽ được xác định tại thời điểm biên dịch.

Ví dụ:

using System;

class MayTinh
{
    public int TinhTong(int a, int b)
    {
        return a + b;
    }

    public double TinhTong(double a, double b)
    {
        return a + b;
    }

    public int TinhTong(int a, int b, int c)
    {
        return a + b + c;
    }
}

class Program
{
    static void Main()
    {
        MayTinh mt = new MayTinh();
        Console.WriteLine(mt.TinhTong(2, 3));       // Output: 5
        Console.WriteLine(mt.TinhTong(2.5, 3.5));   // Output: 6.0
        Console.WriteLine(mt.TinhTong(1, 2, 3));    // Output: 6
    }
}

📌 Ghi nhớ:

  • C# hỗ trợ nạp chồng phương thức (Method Overloading), nhưng phải khác nhau về danh sách tham số (số lượng, kiểu dữ liệu).
  • Không thể nạp chồng chỉ dựa vào kiểu trả về.

3. Đa hình thời gian chạy (Method Overriding)

  • Khi một lớp con ghi đè (override) một phương thức của lớp cha.
  • Dùng từ khóa virtual trong lớp cha và override trong lớp con.
  • Việc chọn phương thức nào để gọi được quyết định khi chương trình chạy (runtime).

Ví dụ:

using System;

class DongVat
{
    public virtual void Keu()
    {
        Console.WriteLine("Động vật đang kêu...");
    }
}

class Cho : DongVat
{
    public override void Keu()
    {
        Console.WriteLine("Gâu gâu!");
    }
}

class Meo : DongVat
{
    public override void Keu()
    {
        Console.WriteLine("Meo meo!");
    }
}

class Program
{
    static void Main()
    {
        DongVat dongVat1 = new Cho();
        DongVat dongVat2 = new Meo();

        dongVat1.Keu(); // Output: Gâu gâu!
        dongVat2.Keu(); // Output: Meo meo!
    }
}

📌 Ghi nhớ:

  • virtual → Cho phép phương thức của lớp cha có thể bị ghi đè.
  • override → Dùng trong lớp con để ghi đè phương thức của lớp cha.
  • Nếu không có override, phương thức của lớp cha sẽ được gọi.

4. Đa hình với lớp trừu tượng (Abstract Class)

  • Lớp trừu tượng (abstract class) chứa các phương thức trừu tượng (abstract method) mà lớp con bắt buộc phải triển khai.
  • Không thể tạo đối tượng từ lớp trừu tượng.

Ví dụ:

using System;

abstract class HinhHoc
{
    public abstract double TinhDienTich();
}

class HinhTron : HinhHoc
{
    public double BanKinh { get; set; }

    public HinhTron(double banKinh)
    {
        BanKinh = banKinh;
    }

    public override double TinhDienTich()
    {
        return Math.PI * BanKinh * BanKinh;
    }
}

class HinhChuNhat : HinhHoc
{
    public double ChieuDai { get; set; }
    public double ChieuRong { get; set; }

    public HinhChuNhat(double chieuDai, double chieuRong)
    {
        ChieuDai = chieuDai;
        ChieuRong = chieuRong;
    }

    public override double TinhDienTich()
    {
        return ChieuDai * ChieuRong;
    }
}

class Program
{
    static void Main()
    {
        HinhHoc hinh1 = new HinhTron(5);
        HinhHoc hinh2 = new HinhChuNhat(4, 6);

        Console.WriteLine($"Diện tích hình tròn: {hinh1.TinhDienTich()}");
        Console.WriteLine($"Diện tích hình chữ nhật: {hinh2.TinhDienTich()}");
    }
}

📌 Ghi nhớ:

  • abstract class → Không thể tạo đối tượng, chỉ dùng để làm lớp cha.
  • abstract method → Lớp con phải override và triển khai nó.

5. Đa hình với Interface

  • Interface chỉ chứa khai báo phương thức, không có triển khai.
  • Một lớp có thể kế thừa nhiều interface (đa kế thừa).

Ví dụ:

using System;

interface IAn
{
    void An();
}

interface INoi
{
    void Noi();
}

class ConNguoi : IAn, INoi
{
    public void An()
    {
        Console.WriteLine("Con người đang ăn...");
    }

    public void Noi()
    {
        Console.WriteLine("Con người đang nói...");
    }
}

class Program
{
    static void Main()
    {
        ConNguoi nguoi = new ConNguoi();
        nguoi.An();
        nguoi.Noi();
    }
}

📌 Ghi nhớ:

  • Interface chỉ có khai báo, không có nội dung phương thức.
  • Một lớp có thể triển khai nhiều interface, giúp đa kế thừa linh hoạt hơn.

Tổng kết

Phương pháp Đặc điểm
Method Overloading Đa hình biên dịch (compile-time)
Method Overriding Đa hình thời gian chạy (runtime)
Abstract Class Dùng khi có phương thức cần triển khai bắt buộc cho lớp con
Interface Định nghĩa hành vi chung, một lớp có thể kế thừa nhiều interface

🔥 Tóm lại: Đa hình giúp code linh hoạt hơn, dễ mở rộng, và hỗ trợ nguyên tắc Lập trình hướng đối tượng (OOP).

Bạn có muốn thêm bài tập không? 😊

Viết chương trình C# sử dụng đa hình (Polymorphism) để quản lý nhân sự trong một công ty. Chương trình gồm các lớp sau:

  1. Lớp cha NhanVien (abstract class) có:

    • Thuộc tính: HoTen, LuongCoBan.
    • Phương thức trừu tượng TinhLuong(), yêu cầu lớp con phải override.
  2. Lớp con NhanVienFullTime kế thừa NhanVien:

    • Có thêm thuộc tính HeSoLuong.
    • Override phương thức TinhLuong() theo công thức: Luong=LuongCoBan×HeSoLuongLuong = LuongCoBan \times HeSoLuongLuong=LuongCoBan×HeSoLuong
  3. Lớp con NhanVienPartTime kế thừa NhanVien:

    • Có thêm thuộc tính SoGioLam, LuongMoiGio.
    • Override phương thức TinhLuong() theo công thức: Luong=SoGioLam×LuongMoiGioLuong = SoGioLam \times LuongMoiGioLuong=SoGioLam×LuongMoiGio
  4. Lớp Program

    • Nhập danh sách nhân viên (Full-time và Part-time).
    • Hiển thị thông tin nhân viên cùng mức lương tương ứng.