Thừa kế trong C# (Inheritance)
Thừa kế
Thừa kế có thể định nghĩa là quá trình mà một lớp có được các tài sản (trường dữ liệu và phương thức hoạt động) của lớp khác. Với việc sử dụng thừa kế, thông tin được quản lý theo thứ tự phân cấp.
Lớp thừa kế các tài sản của lớp khác được biết đến là lớp con (derived class, child class) và lớp cung cấp tài sản cho lớp khác thừa kế tới được gọi là lớp cha (base class, parent class).
Trong C#, chúng ta sử dụng toán tử :
để làm toán tử thừa kế, chỉ ra một lớp là lớp con của một lớp khác.
Một trong những lợi ích quan trọng của việc thừa kế là ngăn ngừa việc trùng lặp mã lệnh vì nó cho phép 1 lớp chia sẻ các trường dữ liệu và phương thức hoạt động với các lớp khác.
Minh hoạ bên dưới sẽ mô tả rõ hơn về thừa kế trong C#:
Triển khai kế thừa trong C#
Để tạo lớp con thừa kế tới một lớp khác chúng ta sử dụng toán tử :
trong dòng định nghĩa lớp. Cú pháp như sau:
public class SuperClass { ... } public class SubClass : SuperClass { ... }
Ví dụ về thừa kế
Tiếp tục quay lại ví dụ CarDemo trong bài “Đối tượng và lớp”, chúng ta bây giờ sẽ phải quản lý nhiều loại xe khác nhau như xe Sedan, Convertible, Electric. Các xe này vẫn có những tính chất và hành động chung của đối tượng Car nhưng sẽ có thêm những tính chất chất và phương thức riêng cho từng loại. Chính vì vậy để tránh việc lặp lại mã lệnh trên từng dòng xe thì chúng ta cần phải biến lớp Car thành lớp cha và cho các ô tô khác thừa kế đến lớp Car
. Cụ thể chúng ta sẽ tạo ra 3 lớp Sedan
, Convertible
và Electric
thông qua ví dụ bên dưới.
Trong Visual Studio Code, các bạn tạo dự án ứng dụng C# mới và đặt tên là CarDemo
. Đầu tiên bạn hãy tạo lớp Car
với nội dung như sau:
using System; namespace CarDemo { public class Car { string make; string model; string color; string vin; public Car() { this.make = ""; this.model = ""; this. color = ""; this.vin = ""; } public void Start() { Console.WriteLine("Car is starting!"); } public void Stop() { Console.WriteLine("Car is stopped!"); } } }
Sau đó xin mời bạn tạo tiếp 3 lớp tương ứng lần lượt là Sedan
, Convertible
và Electric
như sau:
using System; namespace CarDemo { public class Sedan : Car { double speed; public void Boost(double _increase) { this.speed += _increase; } } }
using System; namespace CarDemo { public class Convertible : Car { public void OpenTop() { Console.WriteLine("Car is opening top!"); } } }
using System; namespace CarDemo { public class Electric : Car { public void Charge() { Console.WriteLine("Car is charging!"); } } }
Cuối cùng trong lớp chính Program
, xin mời bạn gõ đoạn mã lệnh sau:
namespace CarDemo { public class Program { public static void Main(string[] args) { // Create an instant object from Electric class Electric electric = new Electric(); // Call methods from Car base class electric.Start(); electric.Stop(); // Call method from derived Electric class electric.Charge(); } } }
Ví dụ CarDemo sau đây sẽ minh hoạ đầy đủ về quá trình thừa kế ở trên:
Trong chương trình trên, sau khi các đối tượng thể hiện của lớp Sedan
, Convertible
, Electric
được tạo thì chúng sẽ có những trường dữ liệu make, model, color và phương thức start()
, stop()
của lớp cha. Ngoài ra chúng sẽ có thêm nhữnng tính chất và phương thức hoạt động riêng của từng lớp con.
Chý ý: Lớp con thừa kế tất các thành phần của của lớp cha (trường dữ liệu và phương thức hoạt động). Tuy nhiên phương thức khởi tạo thì lại không được thừa kế. Chính vì vậy bạn muốn dùng lại phương thức khởi tạo của lớp cha thì bạn phải dùng từ khóa base
.
Từ khoá base
Từ khoá base giống như từ khoá this và được sử dụng trong ngữ cảnh sau đây:
- Nó được sử dụng để phân biệt giữa các trường dữ liệu, phương thức của lớp cha và của lớp con nếu chúng có cùng tên
- Nó được sử dụng để kế thừa phương thức khởi tạo(constructor) của lớp cha từ lớp con
Phân biệt các trường dữ liệu, phương thức của lớp cha và lớp con
Nếu một lớp thừa kế một lớp khác và có trường dữ liệu hoặc phương thức có tên giống nhau thì để phân biệt giữa 2 nhóm trường dữ liệu, phương thức của lớp cha và lớp con thì chúng ta sẽ dùng cú pháp sau đây:
base.variable = ...; base.method();
Tiếp theo chúng ta tiếp tục tìm hiểu ví dụ về từ khoá base
bằng cách tạo một dự án mới trong Visual Studio Code với tên là SuperDemo.
Trong dự án vừa tạo ta sẽ tạo 2 lớp tên là SubClass
and SuperClass
, đồng thời có cùng method tên là display()
với nội dung khác nhau. Ví dụ sau sẽ minh hoạ cách thức gọi phương thức display()
của cả lớp cha và lớp con.
using System; namespace InheritanceDemo { public class SuperClass { public int num = 20; // display method of SuperClass public void Display() { Console.WriteLine("This is the Display method of superclass"); } } }
using System; namespace InheritanceDemo { public class SubClass : SuperClass { public int num = 10; // display method of SubClass public void Display() { Console.WriteLine("This is the Display method of subclass"); } public void RunMethod() { // Initialize sub class SubClass sub = new SubClass(); // call Display() of sub class sub.Display(); // call Display() of super class base.Display(); // display value of num variable in sub class Console.WriteLine("Value of the variable named num in subclass:" + sub.num); // display value of num variable in super class Console.WriteLine("Value of the variable named num in superclass: " + base.num); } } }
using System; namespace SuperDemo { public class Program { public static void Main(string[] args) { SubClass sub = new SubClass(); sub.RunMethod(); } } }
Ví dụ InheritanceDemo sẽ minh hoạ chi tiết về quá trình thừa kế ở trên:
Kế thừa phương thức khởi tạo của lớp cha
Khi 1 lớp con thừa kế đến 1 lớp cha thì lớp con sẽ có những trường dữ liệu và phương thức của lớp cha. Tuy nhiên phương thức khởi tạo lại không được thừa kế. Để có thể sử dụng phương thức khởi tạo của lớp cha trong phương thức khởi tạo của lớp con thì bạn dùng cú pháp sau:
child_class(parameters list):base(parameters list) {};
Vẫn ở dự án trên, bạn hãy cập nhật thêm đoạn mã lệnh sau đây trong các lớp tương ứng:
using System; namespace ConstructorInheritanceDemo { public class SuperClass { public int age; public SuperClass(int age) { this.age = age; } public void GetAge() { Console.WriteLine("The value of the variable named age in superclass is: " + age); } } }
using System; namespace ConstructorInheritanceDemo { public class SubClass : SuperClass { public SubClass(int age) : base(age) { } } }
using System; namespace ConstructorInheritanceDemo { public class Program { public static void Main(string[] args) { SubClass sub = new SubClass(24); sub.GetAge(); } } }
Ví dụ ConstructorInheritanceDemo sau đây sẽ minh hoạ cụ thể cách thức sử dụng thừa kế constructor như diễn giả ở trên:
Từ khoá is
Từ khoá is hay còn gọi là toán tử is
dùng để kiểm tra xác định xem loại của đối tượng đó có phải là dựa trên một lớp cụ thể hay không. Ví dụ IsOperatorDemo sau sẽ giúp hiểu rõ hơn về từ khoá này:
Ép kiểu đối tượng
Như chúng ta đã biết, tất cả các lớp thư viện có sẵn hay do bạn tạo trong ứng dụng Java mặc định đều là lớp con của lớp Object
. Cho nên dù bạn có khởi tạo các đối tượng từ các phương thức khởi tạo khác nhau thì đều có thể chuyển kiểu của đối tượng thể hiện sang kiểu Object
. Ví dụ sau sẽ làm rõ điều này:
Object obj = new Car();
và như vậy là đối tượng thể hiện obj
vừa có kiểu là Object
vừa là kiểu đối tượng Car
(cho đến khi đối tượng obj
được gán bằng 1 đối tượng khác không cùng kiểu với đối tượng Car
). Đây còn gọi là ép kiểu ngầm (ép kiểu không tường minh).
Tương tự như vậy, bạn có thể ép kiểu đối tượng lớp cha về đối tượng lớp con như sau:
Car car = new Car(); Sedan sedan = (Sedan)car;
Chú ý: Để đảm báo quá trình ép kiểu đối tượng không có lỗi do ép nhầm các đối tượng không liên quan đến nhau thì ta nên kiểm tra kiểu của đối tượng có phải là kiểu mong muốn hay không?, rồi sau đó mới tiến hành ép kiểu. Ví dụ sau sẽ giúp bạn hiểu rõ hơn điều này:
if (car is Sedan) { Sedan sedan = (Sedan)car; }
Các tình huống trong quá trình thừa kế
Nếu bạn đã tìm hiểu danh mục các API thư viện của C# thì bạn thấy các lớp thư viện sẽ được tạo theo cách thức thừa kế tài nguyên của nhau. Trong quá trình, phát triển ứng dụng của bạn, chắc chắn bạn cũng sẽ phải tạo ra những lớp nền tảng hay thư viện và sau đó cho phép các lớp khác thừa kế nghiệp vụ của các lớp thư viện. Sau đây, chúng tôi sẽ liệt kê một số cách thức cở bản để triển khai quá trình quá trình thừa kế lớp trong C#:
Lưu ý: Một điều rất quan trọng là C# không hỗ trợ đa thừa kế với các lớp. Điều này có nghĩa là một lớp con chỉ thừa kết tới duy nhất một lớp cha. Để thực hiện quá trình đa thừa kế thì ta phải sử dụng thông qua interface
.