Kế thừa và Đa hình
- 24-11-2023
- Toanngo92
- 0 Comments
Kế thừa là quá trình tạo ra một lớp mới từ một lớp hiện có. Kế thừa cho phép bạn kế thừa các thuộc tính và phương thức của lớp cơ sở vào lớp mới tạo ra. Đa hình là một tính năng của lập trình hướng đối tượng cho phép các thành viên dữ liệu của một lớp hoạt động khác nhau dựa trên tham số và kiểu dữ liệu của chúng.
Trong phiên này, bạn sẽ học được:
- Định nghĩa và mô tả về kế thừa
- Giải thích việc ghi đè phương thức
- Định nghĩa và mô tả về các lớp sealed
- Giải thích về đa hình
Mục lục
Kế thừa
Ở một số trường hợp, một lập trình viên không cần phải tạo ra một lớp trong ứng dụng từ đầu. Đôi khi, người lập trình có thể tạo ra một lớp mới bằng cách mở rộng các tính năng của một lớp hiện có. Quá trình tạo ra một lớp mới bằng cách mở rộng một số tính năng của một lớp hiện có được gọi là kế thừa.
Định nghĩa của Kế thừa
Sự giống nhau về đặc điểm vật lý của một đứa trẻ so với cha mẹ của nó là do đứa trẻ đã kế thừa những đặc điểm này từ cha mẹ. Tương tự, trong C#, kế thừa cho phép bạn tạo ra một lớp bằng cách kế thừa các thuộc tính và phương thức chung của một lớp hiện có.
Lớp từ đó lớp mới được tạo ra được gọi là lớp cơ sở và lớp được tạo ra được gọi là lớp dẫn xuất.
Ví dụ, xem xét một lớp được gọi là Vehicle (Xe cộ) gồm một biến gọi là color (màu sắc) và một phương thức gọi là Speed() (Tốc độ). Những thành viên dữ liệu này của lớp Vehicle có thể được kế thừa bởi các lớp TwoWheelerVehicle (Xe hai bánh) và FourtheelerVehicle (Xe bốn bánh). Hình bên dưới minh họa ví dụ này.
Mục đích
Mục đích của kế thừa là tái sử dụng các phương thức và thuộc tính chung giữa các lớp mà không cần tạo lại chúng. Khả năng tái sử dụng mã cho phép bạn sử dụng cùng một mã trong các ứng dụng khác nhau với ít hoặc không có sửa đổi. Xem xét một lớp có tên là Animal (Động vật) xác định các thuộc tính và hành vi cho động vật. Nếu cần phải tạo ra một lớp mới có tên là Cat (Mèo), nó có thể được thực hiện dựa trên lớp Animal vì mèo cũng là một loại động vật. Do đó, bạn có thể tái sử dụng mã từ lớp đã được xác định trước đó.
Về việc kế thừa, ngoài khả năng tái sử dụng, nó được sử dụng rộng rãi cho:
- Tổng quát hóa
Kế thừa cho phép bạn thực hiện việc tổng quát hóa bằng cách tạo ra các lớp cơ sở. Ví dụ, xem xét lớp Vehicle (Xe cộ), đó là lớp cơ sở cho các lớp dẫn xuất của nó như Truck (Xe tải) và Bike (Xe đạp). Lớp Vehicle bao gồm các thuộc tính và phương thức chung được cài đặt cụ thể hơn trong các lớp dẫn xuất tương ứng.
- Chuyên biệt hóa
Kế thừa cho phép bạn thực hiện việc chuyên biệt hóa bằng cách tạo ra các lớp dẫn xuất.
Ví dụ, các lớp dẫn xuất như Bike (Xe đạp), Bicycle (Xe đạp địa hình), Bus (Xe buýt) và Truck (Xe tải) được chuyên biệt hóa bằng cách thực hiện chỉ các phương thức cụ thể từ lớp cơ sở tổng quát Vehicle.
- Mở rộng
Kế thừa cho phép bạn mở rộng các chức năng của một lớp dẫn xuất bằng cách tạo ra các phương thức và thuộc tính mới không có trong lớp cơ sở. Nó cho phép bạn cung cấp các tính năng bổ sung cho lớp dẫn xuất hiện có mà không cần sửa đổi mã nguồn hiện tại.
Hình bên dưới hiển thị một ví dụ thực tế minh họa mục đích của kế thừa.
Hệ thống Phân cấp Đa cấp
Kế thừa cho phép người lập trình xây dựng các hệ thống phân cấp có thể chứa nhiều cấp độ kế thừa. Ví dụ, xem xét ba lớp Mammal, Animal và Dog. Lớp Mammal được kế thừa từ lớp cơ sở Animal, lớp này kế thừa tất cả các thuộc tính của lớp Animal. Lớp Dog được kế thừa từ lớp Mammal và kế thừa tất cả các thuộc tính của cả lớp Animal và Mammal.
Hình bên dưới mô tả hệ thống phân cấp đa cấp của các lớp liên quan.
Đoạn mã bên dưới thể hiện nhiều cấp độ kế thừa.
using System;
class Animal {
public void Eat() {
Console.WriteLine("Every animal eats something.");
}
}
class Mammal : Animal {
public void Feature() {
Console.WriteLine("Mammals give birth to young ones.");
}
}
class Dog : Mammal {
public void Noise() {
Console.WriteLine("Dog barks.");
}
}
class Program {
static void Main(string[] args) {
Dog objDog = new Dog();
objDog.Eat();
objDog.Feature();
objDog.Noise();
}
}
Trong Đoạn mã trên, phương thức Eat() của lớp Dog gọi các phương thức của lớp Animal, Mammal và Dog.
Kết quả:
Every Animal eats something.
Mammals give birth to young ones.
Dog barks.
Thực hiện Kế thừa
Cú pháp để kế thừa một lớp từ một lớp khác rất đơn giản trong C#. Bạn chỉ cần chèn một dấu hai chấm sau tên của lớp dẫn xuất, tiếp theo là tên của lớp cơ sở. Lớp dẫn xuất sau đó có thể kế thừa tất cả các phương thức và thuộc tính không phải là private của lớp cơ sở.
Cú pháp sau được sử dụng để kế thừa một lớp trong C#:
<DerivedClassName> : <BaseClassName>
trong đó,
- DerivedClassName: Là tên của lớp con mới được tạo ra.
- BaseClassName: Là tên của lớp cha từ đó lớp hiện tại được kế thừa.
Cú pháp sau được sử dụng để gọi một phương thức của lớp cơ sở:
<objectName>.<MethodName>;
trong đó,
- objectName: Là đối tượng của lớp cơ sở.
- MethodName: Là tên của phương thức của lớp cơ sở.
Đoạn mã bên dưới mô tả cách kế thừa một lớp từ một lớp hiện có khác và kế thừa các phương thức từ lớp cơ sở.
using System;
class Animal {
public void Eat() {
Console.WriteLine("Every animal eats something.");
}
public void DoSomething() {
Console.WriteLine("Every animal does something.");
}
}
class Cat : Animal {
static void Main(string[] args) {
Cat objCat = new Cat();
objCat.Eat();
objCat.DoSomething();
}
}
Trong Đoạn mã trên, lớp Animal bao gồm hai phương thức Eat() và DoSomething(). Lớp Cat được kế thừa từ lớp Animal. Một thể hiện của lớp Cat được tạo ra và gọi hai phương thức đã được định nghĩa trong lớp Animal. Dù đã tạo ra một thể hiện của lớp dẫn xuất, nhưng các phương thức được gọi là của lớp cơ sở vì những phương thức này không được định nghĩa lại trong lớp dẫn xuất. Khi thể hiện của lớp Cat gọi các phương thức Eat() và DoSomething(), các câu lệnh trong các phương thức Eat() và DoSomething() của lớp cơ sở Animal được thực thi.
Kết quả:
Every animal eats something.
Every animal does something.
Bộ điều chỉnh truy cập protected
Bộ điều chỉnh truy cập protected bảo vệ các thành viên dữ liệu được khai báo sử dụng bộ điều chỉnh này. Bộ điều chỉnh truy cập protected được chỉ định bằng từ khóa protected. Các biến hoặc phương thức được khai báo với protected chỉ có thể truy cập bởi lớp mà chúng được khai báo hoặc bởi một lớp được kế thừa từ lớp này. Hình bên dưới hiển thị một ví dụ về việc sử dụng bộ điều chỉnh truy cập protected.
Cú pháp sau đây khai báo một biến được bảo vệ:
protected <data_type> <VariableName>;
trong đó,
- data_type: Là kiểu dữ liệu của thành viên dữ liệu.
- VariableName: Là tên của biến.
Cú pháp sau đây khai báo một phương thức được bảo vệ:
protected <return_type> <MethodName>(argument_list);
trong đó,
- return_type: Là kiểu dữ liệu mà phương thức sẽ trả về.
- MethodName: Là tên của phương thức.
- argument_list: Là danh sách các tham số.
Đoạn mã bên dưới minh họa việc sử dụng từ khóa truy cập protected:
class Animal {
protected string Food;
protected string Activity;
}
class Cat : Animal {
static void Main(string[] args) {
Cat objCat = new Cat();
objCat.Food = "Mouse";
objCat.Activity = "laze around";
Console.WriteLine("The Cat loves to eat: " + objCat.Food);
Console.WriteLine("The Cat loves to " + objCat.Activity + ".");
}
}
Trong đoạn mã trên, hai biến được tạo trong lớp Animal với từ khóa protected. Lớp Cat được kế thừa từ lớp Animal. Một thể hiện của lớp Cat được tạo ra và tham chiếu đến hai biến được định nghĩa trong lớp Animal bằng toán tử dấu chấm (.). Trình điều chỉnh truy cập protected cho phép các biến được khai báo trong lớp Animal được truy cập bởi lớp dẫn xuất Cat.
Kết quả:
The Cat loves to eat Mouse
The Cat loves to laze around.
Từ khoá base
Từ khóa base cho phép bạn truy cập các biến và phương thức của lớp cơ sở từ lớp dẫn xuất. Khi bạn kế thừa một lớp, các phương thức và biến được định nghĩa trong lớp cơ sở có thể được khai báo lại trong lớp dẫn xuất. Khi bạn gọi các phương thức hoặc truy cập biến, các thành viên dữ liệu của lớp dẫn xuất được gọi và không phải là thành viên dữ liệu của lớp cơ sở. Trong những tình huống như vậy, bạn có thể truy cập các thành viên của lớp cơ sở bằng từ khóa base.
Bạn không thể sử dụng từ khóa base để gọi các phương thức tĩnh của lớp cơ sở.
Dưới đây là cú pháp được sử dụng để chỉ định từ khóa base:
class <ClassName>
{
<access modifier> <returntype> <BaseMethod> {}
}
class <ClassName1> : <ClassName>
{
base.<BaseMethod>;
}
trong đó,
- <ClassName>: Là tên của lớp cơ sở.
- <access modifier>: Là từ khóa quy định phạm vi truy cập của phương thức hoặc biến.
- <returntype>: Là kiểu dữ liệu mà phương thức sẽ trả về.
- <BaseMethod>: Là tên của phương thức trong lớp cơ sở.
- <ClassName1>: Là tên của lớp dẫn xuất từ lớp cơ sở.
- base: Là từ khóa dùng để truy cập các thành viên của lớp cơ sở.
Hình bên dưới hiển thị một ví dụ về việc sử dụng từ khóa cơ sở.
Từ khoá new
Từ khóa new có thể được sử dụng như một toán tử hoặc như một bộ điều chỉnh trong ngôn ngữ lập trình C#. Toán tử new được sử dụng để tạo một thể hiện của lớp thông qua việc khởi tạo đối tượng. Việc khởi tạo này kích hoạt cuối cùng constructor của lớp. Là một bộ điều chỉnh, từ khóa new được sử dụng để ẩn đi các phương thức hoặc biến của lớp cơ sở được kế thừa trong lớp dẫn xuất. Điều này cho phép bạn định nghĩa lại các phương thức hoặc biến được kế thừa trong lớp dẫn xuất. Do việc định nghĩa lại các thành viên của lớp cơ sở trong lớp dẫn xuất dẫn đến việc các thành viên của lớp cơ sở bị ẩn đi, cách duy nhất để truy cập chúng là sử dụng từ khóa base.
Cú pháp dưới đây mô tả việc sử dụng từ khóa new như một bộ điều chỉnh:
<access modifier> class <ClassName>
{
<access modifier> <return type> <BaseMethod>() {}
}
<access modifier> class <ClassName1> : <ClassName>
{
new <access modifier> void <BaseMethod>() {}
}
trong đó,
- <access modifier>: Xác định phạm vi của lớp hoặc phương thức.
- <return type>: Xác định loại dữ liệu mà phương thức sẽ trả về.
- <ClassName>: Là tên của lớp cơ sở.
- <ClassName1>: Là tên của lớp dẫn xuất.
- new: Là một từ khóa được sử dụng để ẩn phương thức của lớp cơ sở.
Đoạn mã bên dưới tạo một đối tượng sử dụng toán tử new.
Employees objEmp = new Employees();
Ở đây, đoạn mã tạo một thể hiện gọi là objEmp của lớp Employees và gọi constructor của nó.
Đoạn mã bên dưới mô tả việc sử dụng bộ điều chỉnh new để định nghĩa lại các phương thức được kế thừa trong lớp cơ sở.
class Employees {
int _empld = 1;
string _empName = "James Anderson";
int _age = 25;
public void Display() {
Console.WriteLine("Employee ID: " + _empld);
Console.WriteLine("Employee Name: " + _empName);
}
}
class Department : Employees {
int _deptId = 501;
string _deptName = "Sales";
new void Display() {
base.Display();
Console.WriteLine("Department ID: " + _deptId);
Console.WriteLine("Department Name: " + _deptName);
}
static void Main(string[] args) {
Department objDepartment = new Department();
objDepartment.Display();
}
}
Trong đoạn mã trên, lớp Employees khai báo một phương thức có tên là Display(). Phương thức này được kế thừa trong lớp dẫn xuất Department và được điều chỉnh bởi từ khóa new. Từ khóa new ẩn đi phương thức kế thừa Display() đã được định nghĩa trong lớp cơ sở, do đó khi gọi phương thức này, phương thức Display() của lớp dẫn xuất sẽ được thực thi. Tuy nhiên, từ khóa base cho phép bạn truy cập các thành viên của lớp cơ sở. Do đó, các câu lệnh trong phương thức Display() của lớp dẫn xuất và lớp cơ sở đều được thực thi và cuối cùng, Employee ID, Employee Name, Department ID và Department Name được hiển thị trên cửa sổ console.
Kết quả:
Employee ID: 1
Employee Name: James Anderson
Department ID: 501
Department Name: Sales
Kế thừa Constructor
Trong C#, bạn không thể kế thừa các constructor tương tự như cách bạn kế thừa các phương thức. Tuy nhiên, bạn có thể gọi constructor của lớp cơ sở bằng cách khởi tạo đối tượng từ lớp dẫn xuất hoặc lớp cơ sở. Thực thể của lớp dẫn xuất luôn đầu tiên gọi constructor của lớp cơ sở, sau đó mới gọi constructor của lớp dẫn xuất. Ngoài ra, bạn có thể gọi rõ ràng constructor của lớp cơ sở bằng từ khóa base trong phần khai báo constructor của lớp dẫn xuất. Từ khóa base cho phép bạn truyền tham số cho constructor.
Hình bên dưới hiển thị một ví dụ về kế thừa hàm tạo.
Đoạn mã bên dưới gọi hàm tạo của lớp cơ sở một cách rõ ràng bằng cách sử dụng từ khóa base
using System;
class Animal {
public Animal() {
Console.WriteLine("Animal constructor without parameters");
}
public Animal(String name) {
Console.WriteLine("Animal constructor with a string parameter");
}
}
class Canine : Animal {
public Canine() : base("Lion") {
Console.WriteLine("Derived Canine");
}
}
class Details {
static void Main(String[] args) {
Canine objCanine = new Canine();
}
}
Đoạn mã trên cho thấy lớp Animal bao gồm hai constructor, một không có tham số và một có tham số kiểu string. Lớp Canine được kế thừa từ lớp Animal. Lớp dẫn xuất Canine bao gồm một constructor gọi constructor của lớp cơ sở Animal bằng cách sử dụng từ khóa base. Nếu từ khóa base không có tham số trong dấu ngoặc đơn, constructor của lớp Animal không chứa tham số sẽ được gọi. Trong lớp Details, khi constructor của lớp dẫn xuất được gọi, nó sẽ lần lượt gọi constructor có tham số của lớp cơ sở.
Kết quả:
Animal constructor with a string parameter
Derived Canine
Gọi Constructor của lớp cơ sở có tham số
Constructor của lớp dẫn xuất có thể một cách rõ ràng gọi constructor của lớp cơ sở bằng cách sử dụng từ khóa base. Nếu constructor của lớp cơ sở có tham số, từ khóa base sẽ được theo sau bởi giá trị của kiểu được chỉ định trong khai báo constructor. Nếu không có tham số, từ khóa base được theo sau bởi một cặp dấu ngoặc đơn. Đoạn mã bên dưới minh họa cách gọi constructor có tham số trong một hệ thống kế thừa đa cấp.
using System;
class Metals {
string _metalType;
public Metals(string type) {
_metalType = type;
Console.WriteLine("Metal: \t\t" + _metalType);
}
}
class SteelCompany : Metals {
string _grade;
public SteelCompany(string grade) : base("Steel") {
_grade = grade;
Console.WriteLine("Grade: \t\t" + _grade);
}
}
class Automobiles : SteelCompany {
string _part;
public Automobiles(string part) : base("Cast Iron") {
_part = part;
Console.WriteLine("Part: \t\t" + _part);
}
static void Main(string[] args) {
Automobiles objAutomobiles = new Automobiles("Chassis");
}
}
Đoạn mã bên trên cho thấy lớp Automobiles kế thừa từ lớp SteelCompany và lớp SteelCompany kế thừa từ lớp Metals. Trong phương thức Main(), khi một thể hiện của lớp Automobiles được tạo ra, nó kích hoạt constructor của lớp Metals, tiếp theo là constructor của lớp SteelCompany và cuối cùng là constructor của lớp Automobiles.
Kết quả:
Metal: Steel
Grade: Cast Iron
Part: Chas
Ghi đè phương thức
Ghi đè phương thức là một tính năng cho phép lớp dẫn xuất ghi đè hoặc định nghĩa lại các phương thức của lớp cơ sở. Ghi đè một phương thức trong lớp dẫn xuất có thể thay đổi nội dung của phương thức được khai báo trong lớp cơ sở. Do đó, cùng một phương thức với cùng tên và chữ ký được khai báo trong lớp cơ sở có thể được tái sử dụng trong lớp dẫn xuất để xác định một hành vi mới. Điều này làm đảm bảo tính tái sử dụng trong quá trình kế thừa các lớp.
Phương thức được thực hiện trong lớp dẫn xuất từ lớp cơ sở được gọi là Phương thức ghi đè từ lớp cơ sở. Hình bên dưới mô tả về ghi đè phương thức.
Lưu ý: Khi ghi đè một phương thức của lớp cơ sở, bạn cần xem xét phạm vi truy cập của phương thức. Điều này có nghĩa rằng phương thức của lớp cơ sở có phạm vi truy cập ít hơn không thể được ghi đè trong lớp dẫn xuất. Ví dụ, một phương thức private trong lớp cơ sở không thể được ghi đè với phạm vi public trong lớp dẫn xuất.
Các Từ Khóa virtual và override
Bạn có thể ghi đè (override) một phương thức của lớp cơ sở trong lớp dẫn xuất bằng cách sử dụng các từ khóa phù hợp trong C# như virtual và override. Nếu bạn muốn ghi đè một phương thức cụ thể của lớp cơ sở trong lớp dẫn xuất, bạn phải khai báo phương thức đó trong lớp cơ sở bằng từ khóa virtual. Một phương thức được khai báo bằng từ khóa virtual được gọi là phương thức ảo (virtual method).
Trong lớp dẫn xuất, bạn phải khai báo phương thức ảo được kế thừa bằng cách sử dụng từ khóa override. Điều này là bắt buộc cho bất kỳ phương thức ảo nào được kế thừa trong lớp dẫn xuất. Từ khóa override ghi đè phương thức của lớp cơ sở trong lớp dẫn xuất.
Dưới đây là cú pháp để khai báo một phương thức ảo sử dụng từ khóa virtual:
<access_modifier> virtual <return_type> <MethodName> (<parameter_list>);
trong đó,
- access_modifier: Là bộ điều chỉnh truy cập của phương thức, có thể là private, public, protected hoặc internal.
- virtual: Là từ khóa được sử dụng để khai báo một phương thức trong lớp cơ sở có thể được ghi đè bởi lớp dẫn xuất.
- return_type: Là kiểu giá trị mà phương thức sẽ trả về.
- MethodName: Là tên của phương thức ảo.
- parameter_list: Là danh sách tham số của phương thức; nó là tùy chọn.
Dưới đây là cú pháp để ghi đè một phương thức sử dụng từ khóa override:
<access_modifier> override <return_type> <MethodName> (<parameter_list>)
trong đó,
- override: Là từ khóa được sử dụng để ghi đè một phương thức trong lớp dẫn xuất. Đoạn mã bên dưới thể hiện việc áp dụng các từ khóa virtual và override trong lớp cơ sở và lớp dẫn xuất tương ứng.
using System;
class Animal {
public virtual void Eat() {
Console.WriteLine("Every animal eats something");
}
protected void DoSomething() {
Console.WriteLine("Every animal does something");
}
}
class Cat : Animal {
// Override phương thức Eat() của lớp Animal
public override void Eat() {
Console.WriteLine("Cat loves to eat the mouse");
}
public static void Main(string[] args) {
Cat objCat = new Cat();
objCat.Eat();
}
}
Trong Đoạn mã trên, lớp Animal bao gồm hai phương thức: phương thức Eat() với từ khóa virtual và phương thức DoSomething() với từ khóa protected. Lớp Cat được kế thừa từ lớp Animal. Một thể hiện của lớp Cat được tạo và toán tử chấm (.) được sử dụng để gọi các phương thức Eat() và DoSomething(). Phương thức ảo Eat() được ghi đè trong lớp dẫn xuất bằng từ khóa override. Điều này cho phép trình biên dịch C# thực thi mã trong phương thức Eat() của lớp dẫn xuất.
Kết quả:
Cat loves to eat the mouse
Lưu ý: Nếu lớp dẫn xuất cố gắng ghi đè một phương thức không phải là ảo, trình biên dịch C# sẽ tạo ra một lỗi. Nếu bạn không ghi đè các phương thức ảo, trình biên dịch C# sẽ tạo ra một cảnh báo thời gian biên dịch. Tuy nhiên, trong trường hợp này, mã sẽ chạy thành công.
Bạn không thể sử dụng các từ khóa new, static và virtual với từ khóa override.
Gọi phương thức của lớp cơ sở
Việc ghi đè phương thức cho phép lớp dẫn xuất định nghĩa lại các phương thức của lớp cơ sở. Việc định nghĩa lại các phương thức của lớp cơ sở cho phép bạn truy cập vào phương thức mới nhưng không phải phương thức gốc của lớp cơ sở. Đôi khi, bạn có thể muốn cả hai, phương thức của lớp cơ sở cũng như phương thức của lớp dẫn xuất được thực thi. Trong trường hợp này, bạn có thể tạo một thể hiện của lớp cơ sở, cho phép bạn truy cập vào phương thức của lớp cơ sở, và một thể hiện của lớp dẫn xuất, để truy cập vào phương thức của lớp dẫn xuất.
Đoạn mã bên dưới thể hiện cách truy cập một phương thức của lớp cơ sở.
using System;
class Student {
string _studentName = "James";
string _address = "California";
public virtual void PrintDetails() {
Console.WriteLine("Student Name: " + _studentName);
Console.WriteLine("Address: " + _address);
}
}
class Grade : Student {
string _class = "Four";
float _percent = 71.25F;
public override void PrintDetails() {
Console.WriteLine("Class: " + _class);
Console.WriteLine("Percentage: " + _percent);
}
}
class Program {
static void Main(string[] args) {
Student objStudent = new Student();
Grade objGrade = new Grade();
objStudent.PrintDetails();
objGrade.PrintDetails();
}
}
Trong Đoạn mã trên, lớp Student bao gồm một phương thức ảo có tên là PrintDetails(). Lớp Grade kế thừa từ lớp Student và ghi đè phương thức của lớp cơ sở PrintDetails(). Phương thức main() tạo một thể hiện của lớp cơ sở Student và lớp dẫn xuất Grade. Thể hiện của lớp cơ sở Student sử dụng toán tử chấm (.) để gọi phương thức của lớp cơ sở PrintDetails(). Thể hiện của lớp dẫn xuất Grade sử dụng toán tử chấm (.) để gọi phương thức của lớp dẫn xuất PrintDetails().
Kết quả:
Student Name: James
Address: California
Class: Four
Percentage: 71.25
Các Lớp Sealed
Một lớp sealed là một lớp ngăn chặn việc kế thừa. Bạn có thể khai báo một lớp sealed bằng cách đặt từ khóa sealed trước từ khóa class. Từ khóa sealed ngăn chặn việc lớp đó được kế thừa bởi bất kỳ lớp nào khác. Do đó, lớp sealed không thể là lớp cơ sở vì nó không thể được kế thừa bởi bất kỳ lớp nào khác. Nếu một lớp cố gắng kế thừa từ một lớp sealed, trình biên dịch C# sẽ tạo ra một lỗi.
Dưới đây là cú pháp được sử dụng để khai báo một lớp sealed:
sealed class <ClassName>
{
//body of the class
}
trong đó,
- sealed: Là từ khóa được sử dụng để ngăn chặn việc kế thừa một lớp.
- ClassName: Là tên của lớp cần được đóng lại (sealed).
Đoạn mã số bên dưới thể hiện việc sử dụng một lớp được đóng lại (sealed class) trong C#.
sealed class Product {
public int Quantity;
public int Cost;
}
class Goods {
static void Main(string[] args) {
Product objProduct = new Product();
objProduct.Quantity = 50;
objProduct.Cost = 75;
Console.WriteLine("Quantity of the Product: " + objProduct.Quantity);
Console.WriteLine("Cost of the Product: " + objProduct.Cost);
}
}
class Pen : Product {
}
Trong Đoạn mã bên trên, lớp Product được khai báo là sealed (đóng lại). Goods chứa mã để tạo một thể hiện của Product và bao gồm hai biến. Lớp này sử dụng toán tử chấm (.) để gọi các biến được khai báo trong Product. Tuy nhiên, khi lớp Pen cố kế thừa lớp Product đã được đóng lại, trình biên dịch C# sẽ tạo ra một lỗi, như được thể hiện trong hình bên dưới:
Lưu ý: Một lớp sealed không thể có bất kỳ thành viên protected nào. Nếu bạn cố gắng khai báo các thành viên protected trong một lớp sealed, trình biên dịch C# sẽ tạo ra cảnh báo vì các thành viên protected chỉ có thể truy cập bởi các lớp kế thừa. Một lớp sealed không có lớp dẫn xuất vì nó không thể được kế thừa.
Mục đích của các lớp sealed
Xem xét một lớp có tên là SystemInformation chứa các phương thức quan trọng ảnh hưởng đến hoạt động của hệ điều hành. Bạn có thể không muốn bất kỳ bên thứ ba nào kế thừa lớp SystemInformation và ghi đè các phương thức của nó, gây ra vấn đề về bảo mật và bản quyền. Ở đây, bạn có thể khai báo lớp SystemInformation là sealed để ngăn chặn bất kỳ thay đổi nào trong các biến và phương thức của nó.
Hướng dẫn
Các lớp sealed là các lớp bị hạn chế không thể được kế thừa. Danh sách dưới đây mô tả các điều kiện mà một lớp có thể được đánh dấu là sealed.
- Khi việc ghi đè các phương thức của một lớp có thể dẫn đến việc hoạt động không mong đợi của lớp
- Khi bạn muốn ngăn chặn bất kỳ bên thứ ba nào thay đổi lớp của bạn
Phương thức Sealed
Một lớp sealed không thể được kế thừa bởi bất kỳ lớp nào khác. Trong C#, một phương thức không thể được khai báo là sealed. Tuy nhiên, khi lớp dẫn xuất ghi đè một phương thức, biến, thuộc tính hoặc sự kiện của lớp cơ sở, thì phương thức, biến, thuộc tính hoặc sự kiện mới có thể được khai báo là sealed. Việc sealed phương thức mới ngăn chặn việc ghi đè tiếp theo. Một phương thức được ghi đè có thể được sealed bằng cách đặt từ khóa override trước từ khóa sealed.
Cú pháp sau được sử dụng để khai báo một phương thức được ghi đè là sealed:
sealed override <return_type> <MethodName>{}
trong đó,
- return_type: Chỉ định kiểu dữ liệu của giá trị được trả về bởi phương thức.
- MethodName: Chỉ định tên của phương thức được ghi đè.
Đoạn mã bên dưới khai báo một phương thức được ghi đè là Print() là sealed.
class ITsystem {
public virtual void Print() {
Console.WriteLine("The system should be handled carefully");
}
}
class CompanySystem : ITSystem {
public override sealed void Print() {
Console.WriteLine("The system infomation is confidential");
Console.WriteLine("This information should not be overridden");
}
}
class SealedSystem : CompanySystem {
public override void Print() {
Console.WriteLine("This statement won't get executed");
}
}
static void Main(string[] args) {
SealedSystem objSealed = new SealedSystem();
objSealed.Print();
}
Trong Đoạn mã trên, lớp ITSystem bao gồm một hàm ảo Print(). Lớp CompanySystem được kế thừa từ lớp ITSystem. Nó ghi đè phương thức của lớp cơ sở là Print().
Phương thức được ghi đè Print() đã được sealed bằng cách sử dụng từ khóa sealed, từ đó ngăn chặn việc ghi đè tiếp theo của phương thức đó. Lớp SealedSystem được kế thừa từ lớp CompanySystem.
Khi lớp SealedSystem ghi đè phương thức sealed Print(), trình biên dịch C# sẽ tạo ra một lỗi, như được thể hiện trong hình bên dưới.
Đa hình (Polymorphism)
Đa hình được xuất phát từ hai từ tiếng Hy Lạp, gồm Poly và Morphos. Poly có nghĩa là nhiều và Morphos có nghĩa là hình dạng. Đa hình có nghĩa là tồn tại dưới nhiều dạng. Đa hình là khả năng của một thực thể để hoạt động khác nhau trong các tình huống khác nhau. Hãy xem xét hai phương thức sau trong một lớp có cùng tên nhưng có các chữ ký khác nhau thực hiện cùng một hoạt động cơ bản nhưng theo cách khác nhau:
- Area(float radius)
- Area(float base, float height)
Hai phương thức này tính diện tích của hình tròn và hình tam giác bằng cách sử dụng các tham số khác nhau và công thức khác nhau. Đây là một ví dụ về đa hình trong C#.
Đa hình cho phép các phương thức hoạt động khác nhau dựa trên các tham số và kiểu dữ liệu của chúng.
Triển khai
Bạn có thể triển khai đa hình trong C# thông qua nạp chồng phương thức (method overloading) và ghi đè phương thức (method overriding). Bạn có thể tạo nhiều phương thức có cùng tên trong một lớp hoặc trong các lớp khác nhau có cùng tên nhưng có thân phương thức khác nhau hoặc chữ ký khác nhau. Các phương thức có cùng tên nhưng có các chữ ký khác nhau trong một lớp được gọi là các phương thức được nạp chồng. Ở đây, cùng một phương thức thực hiện cùng một chức năng trên các giá trị khác nhau.
Các phương thức được kế thừa từ lớp cơ sở vào lớp dẫn xuất và được sửa đổi trong lớp dẫn xuất được gọi là các phương thức được ghi đè. Ở đây, chỉ có thân phương thức thay đổi để hoạt động theo đúng đầu ra yêu cầu.
Hình bên dưới hiển thị việc triển khai.
Đoạn mã số bên dưới thể hiện việc sử dụng tính năng nạp chồng phương thức.
class Area {
static int CalculateArea(int len, int wide) {
return len * wide;
}
static double CalculateArea(double valOne, double valTwo) {
return 0.5 * valOne * valTwo;
}
static void Main(string[] args) {
int length = 10;
int breadth = 22;
double baseValue = 2.5;
double height = 1.5;
Console.WriteLine("Area of Rectangle: " + CalculateArea(length, breadth));
Console.WriteLine("Area of triangle: " + CalculateArea(baseValue, height));
}
}
Trong Đoạn mã số trên, lớp Area bao gồm hai phương thức tĩnh cùng tên là CalculateArea. Tuy nhiên, cả hai phương thức này có các kiểu trả về khác nhau và nhận các tham số khác nhau.
Kết quả:
Area of Rectangle: 220
Area of triangle: 1.875
Biên dịch và chạy Đa hình
Đa hình có thể được phân loại chung thành hai loại, đa hình biên dịch và đa hình thời gian chạy. Bảng bên dưới phân biệt giữa đa hình biên dịch và đa hình thời gian chạy.
Đa hình Biên dịch | Đa hình Thời gian chạy |
Được thực hiện thông qua việc nạp chồng phương thức. | Được thực hiện thông qua ghi đè phương thức. |
Được thực thi tại thời điểm biên dịch kể từ khi trình biên dịch biết phương thức nào để thực thi tùy thuộc vào số lượng tham số và kiểu dữ liệu của chúng. | Được thực thi trong thời gian chạy vì trình biên dịch không biết phương thức được thực thi, cho dù đó là phương thức lớp cơ sở sẽ được gọi hoặc phương thức lớp dẫn xuất. |
Được gọi là đa hình tĩnh. | Được gọi là đa hình động. |
Đoạn mã số bên dưới thể hiện việc triển khai đa hình thời gian chạy.
class Circle {
protected const double PI = 3.14;
protected double Radius = 14.9;
public virtual double Area() {
return PI * Radius * Radius;
}
}
class Cone : Circle {
protected double Side = 10.2;
public override double Area() {
return PI * Radius * Side;
}
}
static void Main(string[] args) {
Circle objRunOne = new Circle();
Console.WriteLine("Area is: " + objRunOne.Area());
Circle objRunTwo = new Cone();
Console.WriteLine("Area is: " + objRunTwo.Area());
}
Trong đoạn mã trên, lớp Circle khởi tạo các biến được bảo vệ và chứa một phương thức ảo Area() trả về diện tích của hình tròn. Lớp Cone được kế thừa từ lớp Circle, nơi phương thức Area() được ghi đè. Phương thức Area() trả về diện tích của hình nón bằng cách xem xét độ dài của hình nón, được khởi tạo với giá trị 10.2. Phương thức Main() thể hiện cách đa hình có thể diễn ra bằng cách tạo đối tượng kiểu Circle đầu tiên và gọi phương thức Area() của nó, sau đó tạo một tham chiếu kiểu Circle nhưng khởi tạo nó thành Cone vào thời gian chạy, sau đó gọi phương thức Area(). Trong trường hợp này, phương thức Area() của Cone sẽ được gọi dù tham chiếu được tạo ra ban đầu là của Circle.
Kết quả:
Area is: 697.1114
Area is: 477.2172