Menu
CoddyTech

Từ khóa virtual và override

Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 21 trên 70.

Khi một lớp dẫn xuất kế thừa một phương thức, đôi khi bạn muốn thay đổi hoàn toàn cách phương thức đó hoạt động. Các từ khóa virtual và override phối hợp với nhau để giúp bạn thực hiện điều này một cách có kiểm soát.

Đánh dấu một phương thức là virtual trong lớp cơ sở để cho biết các lớp dẫn xuất có thể thay thế phần triển khai của phương thức đó:

public class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Some sound");
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof!");
    }
}

Từ khóa override trong lớp dẫn xuất cho trình biên dịch biết bạn đang chủ ý thay thế phương thức của lớp cơ sở. Nếu phương thức của lớp cơ sở không có virtual, bạn không thể dùng override.

Sức mạnh thực sự thể hiện khi bạn dùng tham chiếu đến lớp cơ sở để giữ một đối tượng thuộc lớp dẫn xuất:

Animal myPet = new Dog();
myPet.Speak();  // Đầu ra: Woof!

Mặc dù myPet được khai báo là Animal, C# gọi phiên bản Dog của Speak(). Đây gọi là đa hình lúc chạy: phương thức thực sự được gọi phụ thuộc vào kiểu thực của đối tượng, không phải kiểu khai báo của biến.

Bạn vẫn có thể truy cập phần triển khai của lớp cơ sở bằng base.Speak() bên trong phương thức ghi đè nếu muốn mở rộng hành vi thay vì thay thế hoàn toàn.

challenge icon

Thử thách

Dễ

Hãy cùng xây dựng một hệ thống thông báo để minh họa cách các lớp dẫn xuất có thể ghi đè hoàn toàn hành vi của lớp cơ sở bằng từ khóa virtual và override, đồng thời cho thấy sức mạnh của tính đa hình lúc chạy.

Bạn sẽ tạo ba tệp để sắp xếp code:

  • Notification.cs: Định nghĩa lớp Notification trong không gian tên Alerts. Lớp cơ sở này đại diện cho một thông báo chung, có thuộc tính Message (chuỗi) và hàm khởi tạo để thiết lập thuộc tính đó. Thêm phương thức virtual có tên Send(), trả về "Sending notification: {Message}".
  • EmailNotification.cs: Định nghĩa lớp EmailNotification trong không gian tên Alerts, kế thừa từ Notification. Thêm thuộc tính Recipient (chuỗi) và hàm khởi tạo nhận cả thông báo lẫn người nhận (dùng base để truyền thông báo). Ghi đè phương thức Send() để trả về "Emailing {Recipient}: {Message}".
  • SmsNotification.cs: Định nghĩa lớp SmsNotification trong không gian tên Alerts, cũng kế thừa từ Notification. Thêm thuộc tính PhoneNumber (chuỗi) và hàm khởi tạo nhận cả thông báo lẫn số điện thoại. Ghi đè Send() để trả về "SMS to {PhoneNumber}: {Message}".
  • Program.cs: Trong tệp chính, tạo instance của cả ba loại thông báo bằng các giá trị đầu vào. Sau đó, minh họa tính đa hình lúc chạy bằng cách lưu cả ba vào các biến kiểu Notification và gọi Send() trên từng biến.

Bạn sẽ nhận bốn giá trị đầu vào:

  • Nội dung thông báo
  • Địa chỉ email của người nhận
  • Số điện thoại để gửi SMS

In kết quả gọi Send() trên từng thông báo, được lưu dưới dạng tham chiếu đến lớp cơ sở:

{base notification Send() result}
{email notification Send() result}
{sms notification Send() result}

Ví dụ, nếu các giá trị đầu vào là Meeting at 3pm, alice@email.com và 555-1234, kết quả sẽ là:

Sending notification: Meeting at 3pm
Emailing alice@email.com: Meeting at 3pm
SMS to 555-1234: Meeting at 3pm

Hãy chú ý rằng dù cả ba đều được lưu trong các biến kiểu Notification, mỗi biến vẫn gọi phiên bản Send() được ghi đè riêng của nó. Đó chính là tính đa hình lúc chạy!

Tự làm thử

using System;
using Alerts;

class Program
{
    public static void Main(string[] args)
    {
        // Đọc các đầu vào
        string message = Console.ReadLine();
        string email = Console.ReadLine();
        string phone = Console.ReadLine();

        // TODO: Tạo instance của cả ba loại thông báo
        // Lưu chúng vào các biến kiểu Notification để minh họa tính đa hình
        
        // TODO: Tạo một Notification cơ sở
        Notification baseNotification;
        
        // TODO: Tạo một EmailNotification (lưu trong biến kiểu Notification)
        Notification emailNotification;
        
        // TODO: Tạo một SmsNotification (lưu trong biến kiểu Notification)
        Notification smsNotification;
        
        // TODO: Gọi Send() trên từng instance và in ra kết quả
        // Nhờ tính đa hình khi chạy, chương trình sẽ gọi các phương thức ghi đè
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C# online