Từ polymorphism (tính đa hình) nghĩa là có nhiều hình thái. Trong lập trình hướng đối tượng, tính đa hình thường được diễn đạt như là "một Interface, nhiều hàm".
Tính đa hình trong C# có thể là static hoặc dynamic. Trong đó, kiểu đa hình static có thể được gọi là đa hình tĩnh, phản hồi tới một hàm được xác định tại thời điểm biên dịch và kiểu đa hình dynamic có thể được gọi là đa hình động, được xác định trong thời gian chạy.
Đa hình static trong C#
Kỹ thuật liên kết một hàm với một đối tượng trong thời gian biên dịch được gọi là Early Binding. Nó cũng được gọi là Static Binding. C# cung cấp hai kỹ thuật để triển khai đa hình tĩnh, là:
- Nạp chồng hàm (Function overloading)
- Nạp chồng toán tử (Operator overloading)
Chúng ta có riêng một bài bàn luận về nạp chồng toán tử trong C#.
Nạp chồng hàm trong C#
Bạn có thể có nhiều định nghĩa cho cùng tên hàm trong cùng một phạm vi. Các định nghĩa này của hàm phải khác nhau: như kiểu và/hoặc số lượng tham số trong danh sách tham số. Trong C#, bạn không thể nạp chồng các khai báo hàm mà chỉ khác nhau ở kiểu trả về.
Ví dụ sau minh họa cách sử dụng hàm print() để in các kiểu dữ liệu khác nhau trong C#:
using System; namespace VdNapChong { class InDuLieu { void print(int i) { Console.WriteLine("In số nguyên: {0}", i ); } void print(double f) { Console.WriteLine("In số thập phân: {0}" , f); } void print(string s) { Console.WriteLine("In chuỗi: {0}", s); } static void Main(string[] args) { InDuLieu p = new InDuLieu(); // Gọi hàm in số nguyên p.print(9); // Gọi hàm in số thập phân p.print(501.263); // Gọi hàm in chuỗi p.print("Học C# thật vui!"); Console.ReadKey(); } } }
Nếu đã đọc các bài C# trước bạn sẽ biết rằng, nếu không sử dụng lệnh Console.ReadKey(); thì chương trình sẽ chạy và kết thúc luôn (quá nhanh đến nỗi bạn không kịp nhìn kết quả). Lệnh này cho phép chúng ta nhìn kết quả một cách rõ ràng hơn.
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
In số nguyên: 9
In số thập phân: 501.263
In chuỗi: Học C# thật vui!
Đa hình dynamic trong C#
C# cho phép bạn tạo các lớp abstract (trừu tượng) mà được sử dụng để cung cấp trình triển khai cục bộ lớp của một Interface. Trình triển khai (Implementation) được hoàn thành khi một lớp dẫn xuất kế thừa từ nó. Các lớp Abstract chứa các phương thức abstract được triển khai bởi lớp dẫn xuất. Lớp dẫn xuất này có chức năng chuyên biệt hơn.
Dưới đây là một số qui tắc về các lớp abstract trong C#:
- Bạn không thể tạo một Instance (sự thể hiện) của một lớp abstract.
- Bạn không thể khai báo một phương thức abstract ở bên ngoài một lớp abstract.
- Khi một lớp được khai báo là kín (sealed), nó không thể được dẫn xuất, các lớp abstract không thể được khai báo là sealed.
Ví dụ sau minh họa một lớp abstract trong C#: tạo 3 lớp có tên lần lượt là Shape, HinhChuNhat, TimDienTich như sau:
using System; namespace VdLopAbstract {
// Lớp Shape là một lớp abstract abstract class Shape { public abstract int area(); } // Lớp HinhChuNhat là lớp dẫn xuất từ lớp Shape class HinhChuNhat: Shape { private int dai; private int rong; public HinhChuNhat( int a = 0, int b = 0) { dai = a; rong = b; } public override int area () { Console.WriteLine("Tính diện tích hình chữ nhật"); return (dai * rong); } }
// Lớp TimDienTich chứa phương thức main() để thao tác trên HinhChuNhat class TimDienTich { static void Main(string[] args) { HinhChuNhat r = new HinhChuNhat(10, 9); double a = r.area(); Console.WriteLine("Diện tích là: {0}",a); Console.ReadKey(); } } }
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
Tính diện tích hình chữ nhật
Diện tích là: 90
Khi bạn có một hàm được định nghĩa trong một lớp mà bạn muốn được triển khai một lớp được kế thừa, bạn sử dụng hàm virtual trong C#. Các hàm virtual có thể được triển khai theo cách khác nhau trong lớp được kế thừa khác nhau và việc gọi những hàm này sẽ được quyết định tại runtime.
Đa hình động trong C# được triển khai bởi các lớp abstract và các hàm virtual.
Ví dụ sau minh họa điều này: tạo 5 lớp có tên lần lượt là như sau Hinh, HinhChuNhat, HinhTamGiac, HienThiDienTich và TimDienTich.
using System; namespace VdLopAbstract {
// Lớp Hinh là lop abstract. class Hinh { protected int dai, rong; public Hinh( int a = 0, int b = 0) { dai = a; rong = b; } public virtual int area() { Console.WriteLine("Diện tích của lớp cha là:"); return 0; } }
// Lớp HinhChuNhat kế thừa từ lớp Hinh. class HinhChuNhat: Hinh { public HinhChuNhat( int a = 0, int b = 0): base(a, b) { } public override int area () { Console.WriteLine("Diện tích lớp HinhChuNhat là:"); return (dai * rong); } }
// Lớp HinhTamGiac kế thừa từ lớp Hình class HinhTamGiac: Hinh { public HinhTamGiac(int a = 0, int b = 0): base(a, b) { } public override int area() { Console.WriteLine("Diện tích lớp HinhTamGiac là:"); return (dai * rong / 2); } }
// In dữ liệu diện tích ra màn hình. class HienThiDienTich { public void CallArea(Hinh sh) { int a; a = sh.area(); Console.WriteLine("Diện tích: {0}", a); } }
// TimDienTich chứa main() thao tác với các đối tượng. class TimDienTich { static void Main(string[] args) { HienThiDienTich c = new HienThiDienTich(); HinhChuNhat r = new HinhChuNhat(10, 7); HinhTamGiac t = new HinhTamGiac(10, 5); c.CallArea(r); c.CallArea(t); Console.ReadKey(); } } }
Biên dịch và chạy chương trình C# trên sẽ cho kết quả sau:
Diện tích lớp HinhChuNhat là:
Diện tích: 70
Diện tích lớp HinhTamGiac là:
Diện tích: 25
Theo tutorialspoint
Bài trước: Tính kế thừa trong C#
Bài tiếp: Nạp chồng toán tử trong C#