Menu
CoddyTech

Đa hình lúc biên dịch và lúc chạy

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 25 trên 70.

Đa hình nghĩa là “nhiều hình thức”, tức khả năng cùng một đoạn code hoạt động khác nhau tùy theo ngữ cảnh. C# hỗ trợ hai kiểu đa hình riêng biệt, và hiểu rõ thời điểm mỗi kiểu được xác định sẽ giúp bạn viết code linh hoạt hơn.

Đa hình tại thời điểm biên dịch (còn gọi là đa hình tĩnh) được trình biên dịch xử lý trước khi chương trình chạy. Nạp chồng phương thức là ví dụ chính: nhiều phương thức dùng chung tên nhưng có các tham số khác nhau:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Trình biên dịch chọn phiên bản int
calc.Add(5.0, 3.0);   // Trình biên dịch chọn phiên bản double

Trình biên dịch (compiler) kiểm tra kiểu dữ liệu của các đối số (argument) và chọn phương thức (method) phù hợp ngay khi biên dịch (compile time). Quyết định này được cố định trước khi chương trình chạy.

Đa hình thời gian chạy (đa hình động) được phân giải khi chương trình chạy. Điều này xảy ra với các phương thức virtual và override, kiểu thực tế của đối tượng sẽ quyết định phương thức nào được thực thi:

Animal pet = new Dog();  // Khai báo là Animal, thực tế là Dog
pet.Speak();             // Gọi phương thức Speak() của Dog khi chạy

Trình biên dịch không biết Speak() nào sẽ chạy. Quyết định đó được đưa ra trong thời gian chạy, dựa trên kiểu thực sự của đối tượng. Đây là điều khiến tính đa hình dựa trên kế thừa trở nên mạnh mẽ, giúp xây dựng các hệ thống linh hoạt và dễ mở rộng.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống chuyển đổi để minh họa cả hai kiểu đa hình. Bạn sẽ tạo một lớp dùng nạp chồng phương thức (đa hình tại thời điểm biên dịch), cùng với hệ thống phân cấp kế thừa dùng các phương thức virtual/override (đa hình tại thời điểm chạy).

Bạn sẽ sắp xếp code trong bốn tệp:

  • Converter.cs: Định nghĩa lớp Converter trong không gian tên Conversion. Lớp này minh họa đa hình tại thời điểm biên dịch thông qua nạp chồng phương thức. Tạo ba phương thức Convert được nạp chồng:
    • Convert(int value) trả về "Integer: {value}"
    • Convert(double value) trả về "Double: {value}"
    • Convert(string value) trả về "String: {value}"
  • Shape.cs: Định nghĩa lớp cơ sở Shape trong không gian tên Conversion. Lớp này có thuộc tính Name (chuỗi) và hàm khởi tạo gán giá trị cho thuộc tính đó. Thêm phương thức virtual có tên Describe(), trả về "This is a {Name}".
  • Circle.cs: Định nghĩa lớp Circle trong không gian tên Conversion, kế thừa từ Shape. Thêm thuộc tính Radius (số thực). Hàm khởi tạo nhận bán kính và truyền "Circle" cho hàm khởi tạo của lớp cơ sở. Ghi đè Describe() để trả về "This is a Circle with radius {Radius}".
  • Program.cs: Trong tệp chính, minh họa cả hai kiểu đa hình. Trước tiên, tạo một Converter và gọi cả ba phương thức được nạp chồng với các giá trị đầu vào. Sau đó, tạo một Circle, lưu đối tượng đó vào một biến kiểu Shape và gọi Describe() để thể hiện đa hình tại thời điểm chạy.

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

  • Một giá trị số nguyên
  • Một giá trị số thực kiểu double
  • Một giá trị chuỗi
  • Bán kính hình tròn (double)

In đầu ra theo định dạng sau:

Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}

Ví dụ, nếu đầu vào là 42, 3.14, Hello và 5.5, đầu ra sẽ là:

Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5

Lưu ý điểm khác biệt chính: trình biên dịch quyết định sẽ gọi phương thức Convert nào dựa trên kiểu đối số tại thời điểm biên dịch, còn phương thức Describe() được xác định tại thời điểm chạy dựa trên kiểu thực tế của đối tượng, dù đối tượng đó được lưu trong một biến Shape!

Tự làm thử

using System;
using Conversion;

class Program
{
    public static void Main(string[] args)
    {
        // Đọc các giá trị đầu vào
        int intValue = Convert.ToInt32(Console.ReadLine());
        double doubleValue = Convert.ToDouble(Console.ReadLine());
        string stringValue = Console.ReadLine();
        double radius = Convert.ToDouble(Console.ReadLine());

        // TODO: Minh họa tính đa hình tại thời điểm biên dịch
        // Tạo đối tượng Converter và gọi cả ba phương thức Convert được nạp chồng
        Console.WriteLine("Compile-time Polymorphism:");
        // Gọi Convert với intValue, doubleValue và stringValue

        // TODO: Minh họa tính đa hình tại thời điểm chạy
        // Tạo đối tượng Circle và lưu vào biến Shape
        // Sau đó gọi Describe() qua biến Shape
        Console.WriteLine("Runtime Polymorphism:");
        // Tạo Circle và gọi Describe()
    }
}
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