Lập trình C#

Tính kế thừa trong lập trình C#

Tính kế thừa trong lập trình C#
Được viết bởi Minh Hoàng

Series lập trình C#, ngôn ngữ lập trình hiện đại và mạnh mẽ.

Trong bài viết này chúng ta sẽ cùng tìm hiểu về tính kế thừa trong lập trình C#, với những nội dung sau:

  1. Tính kế thừa là gì?
  2. Ý nghĩa của Tính kế thừa
  3. Cú pháp khai báo và đặc điểm của Tính kế thừa
    • #1. Cú pháp khai báo.
    • #2. Đặc điểm của tính kế thừa.
  4. Ví dụ minh họa về Tính kế thừa
  5. Hàm dựng Constructor trong kế thừa
    • #1. Trường hợp hàm constructor không tham số.
    • #2. Trường hợp hàm constructor có tham số.
1. Tính kế thừa là gì?
1. Tính kế thừa là gì?

– Một trong những khái niệm quan trọng nhất trong lập trình hướng đối tượng là Tính kế thừa (Inheritance). Tính kế thừa cho phép tạo ra một lớp mới kế thừa Thuộc tínhPhương thức của một lớp khác. Sau đó có thể xây dựng thêm các thuộc tính và phương thức riêng của lớp mới đó.

Thuộc tính là đặc tính riêng vốn có của một sự vật, qua đó con người nhận thức được sự vật, phân biệt được sự vật này với sự vật khác. Thường là tính từ hoặc danh từ như màu sắc, mắt, mũi, tên, tuổi,…

Phương thức là cách thức và phương pháp để thực hiện một hành động nào đó. Thường là động từ như chạy, nhảy, ngủ,…

Lớp cha(lớp đang tồn tại) cho phép một một lớp khác kế thừa được gọi là lớp cơ sởBase Class, và lớp con(lớp mới) được gọi là lớp thừa kế hay lớp dẫn xuấtDerived Class.

2. Ý nghĩa của Tính kế thừa
2. Ý nghĩa của Tính kế thừa
  • Tính nhất quán: những đối tượng có chung nguồn gốc thì sẽ có những đặc điểm chung giống nhau.
  • Tái sử dụng code: những phần code chung chỉ cần định nghĩa một lần tại lớp cha(Base Class), các lớp con(Derived Class) đều có thể sử dụng mà không cần viết lại.
  • Thuận tiện trong việc bảo trì và phát triển. Khi sửa lỗi hay nâng cấp chỉ cần định nghĩa lại ở lớp cha.
Tính kế thừa được minh họa như sau:

Không kế thừa

Không kế thừa


Kế thừa - Inheritance

Kế thừa – Inheritance

・ Ở hình trên các bạn có thể thấy 2 lớp Con trâuCon cún có chung các thuộc tính: Mắt, Chân và các phương thức: Chạy, Ngủ. Đó đều là những đặc điểm chung của một động vật.

・ Do đó chúng ta sẽ tạo một lớp Động vật định nghĩa một lần những đặc điểm chung này rồi cho 2 lớp Con trâuCon cún kế thừa. Khi sử dụng chỉ cần gọi ra từ lớp Động vật lấy dữ liệu xử lý mà không cần viết riêng ở mỗi lớp gây ra sự trùng lặp mã lệnh.

・ Sau khi kế thừa những đặc điểm chung từ lớp Động vật, thì ứng với mỗi lớp Con trâu hoặc Con cún sẽ xây dựng thêm các thuộc tính và phương thức riêng của nó.

3. Cú pháp khai báo và đặc điểm của Tính kế thừa
3. Cú pháp khai báo và đặc điểm của Tính kế thừa

■ Cú pháp khai báo kế thừa trong C#:

[<quyen_truy_cap>] class <Ten_lop_cha>
{
   ...
}

[<quyen_truy_cap>] class <Ten_lop_con> : <Ten_lop_cha>
{
   ...
}

Trong đó:
quyen_truy_cap : là public hay private, protected,… Có thể có hoặc không.
Ten_lop_cha : Tên lớp cơ sở – Base Class
Ten_lop_con : Tên lớp dẫn xuất – Derived Class
・ Dấu hai chấm : biểu diễn sự kế thừa giữa 2 lớp trong C#

■ Đặc điểm của Tính kế thừa:

– Chỉ cho phép đơn kế thừa, tức là một lớp chỉ được phép kế thừa từ một lớp khác. (Vậy làm thế nào một lớp có thể sử dụng được các thuộc tính và phương thức của nhiều lớp khác nhau? Vấn đề này sẽ được trình bày trong bài Interface trong lập trình C#).

– Lớp con chỉ kế thừa được, chỉ sử dụng được các thuộc tính và phương thức có phạm vi truy cập là public, internal hoặc protected của lớp cha.

– Một đối tượng thuộc lớp cha có thể tham chiếu đến một đối tượng thuộc lớp con, nhưng ngược lại thì không được.

// Tạo đối tượng con cún
ConCun cun1 = new ConCun();		// OK

DongVat cun2 = new ConCun();	// OK

//ConCun cun3 = new DongVat();	// ERROR: Cannot implicitly convert type 'DongVat' to 'ConCun'

– Từ khóa base tương tự như từ khóa super trong java dùng để truy cập đến các thành phần bên trong lớp cha từ lớp con.

・ Không dùng từ khóa base cho các thành phần(thuộc tính và phương thức) static, vì những thành phần này sẽ được truy cập trực tiếp bằng tên lớp.

– Từ khóa new trong kế thừa không phải là toán tử dùng để khởi tạo đối tượng, mà nó là một modifier dùng để xác định phạm vi của phương thức.

・ Trong trường hợp vì một lý do nào đó, bạn muốn tạo một phương thức ở lớp con có tên giống với tên của một phương thức ở lớp cha, thì để tránh nhập nhằng giữa 2 phương thứ này, bạn cần gắn từ khóa new cho phương thức ở lớp con.

Ví dụ bên dưới sẽ minh họa cách sử dụng từ khóa base, new, và cách gọi phương thức tĩnh (là phương thức khai báo có sử dụng từ khóa static) bằng tên lớp.

*** Ví dụ ①

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng Con Cún
			ConCun cun = new ConCun();

			// Gọi phương thức Chạy
			cun.Chay();

			// Gọi phương thức tĩnh bằng tên lớp
			DongVat.Ngu();

			Console.ReadKey();
		}
	}

	/// <summary>
	/// Class cơ sở Động Vật
	/// </summary>
	class DongVat
	{
		public void Chay()
		{
			Console.WriteLine( "Dong vat chay." );
		}

		// Phương thức tĩnh
		public static void Ngu()
		{
			Console.WriteLine( "Dong vat ngủ." );
		}
	}

	/// <summary>
	/// Class dẫn xuất Con Cún
	/// </summary>
	class ConCun : DongVat     // Kế thừa
	{
		// Phương thức CÙNG TÊN với lớp cha
		public new void Chay()
		{
			// Gọi hàm từ lớp cha
			base.Chay();

			Console.WriteLine( "Con cun chay." );
		}
	}
}

・ Kết quả chương trình:

Dong vat chay.

Con cun chay.

Dong vat ngủ.
4. Ví dụ minh họa về Tính kế thừa
4. Ví dụ minh họa về Tính kế thừa

Giả sử chúng ta có một lớp cơ sở Hình Học và hai lớp dẫn xuất là Hình Chữ NhậtHình Vuông như sau:

– Lớp cơ sở Hình học : có các thuộc tính Chiều dài, Chiều rộng, và các phương thức Set chiều dài, Set chiều rộng.

– Hai lớp dẫn xuất là Hình Chữ NhậtHình Vuông : ngoài việc kế thừa các thuộc tính và phương thức của lớp cha Hình Học, thì hình chữ nhật và hình vuông sẽ có cách tính chu vi và diện tích là khác nhau nên ở mỗi lớp sẽ xây dựng thêm 2 phương thức riêng là Tính chu viTính diện tích.

Inheritance: Shape - Rectangle - Square

Inheritance: Shape – Rectangle – Square

– Chúng ta sẽ cài đặt như sau:

*** Ví dụ ②

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// ① Tạo đối tượng hình chữ nhật
			HinhChuNhat hcn = new HinhChuNhat();

			// Sử dụng phương thức kế thừa từ lớp cha
			hcn.setChieuRong(8);
			hcn.setChieuDai(9);

			// In Chu vi và Diện tích của hình chữ nhật
			Console.WriteLine("Chu vi hinh chu nhat: {0}", hcn.TinhChuVi());
			Console.WriteLine("Dien tich hinh chu nhat: {0}", hcn.TinhDienTich());

			// ② Tạo đối tượng hình vuông
			HinhVuong hv = new HinhVuong();

			// Sử dụng phương thức kế thừa từ lớp cha
			hv.setChieuDai(19);

			// In Chu vi và Diện tích của hình vuông
			Console.WriteLine("Chu vi hinh vuong: {0}", hv.TinhChuVi());
			Console.WriteLine("Dien tich hinh vuong: {0}", hv.TinhDienTich());

			Console.ReadKey();
		}
	}

	/// <summary>
	/// Class cơ sở Hình Học
	/// </summary>
	class HinhHoc
	{
		// Thuộc tính dùng chung
		protected int ChieuDai;

		protected int ChieuRong;

		// Phương thức dùng chung
		public void setChieuDai(int inChieuDai)
		{
			ChieuDai = inChieuDai;
		}

		public void setChieuRong(int inChieuRong)
		{
			ChieuRong = inChieuRong;
		}
	}

	/// <summary>
	/// Class dẫn xuất Hình Chữ Nhật
	/// </summary>
	class HinhChuNhat : HinhHoc		// Kế thừa
	{
		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai, ChieuRong kế thừa từ lớp cha
			return (ChieuDai + ChieuRong) * 2;
		}

		public int TinhDienTich()
		{
			return (ChieuDai * ChieuRong);
		}
	}

	/// <summary>
	/// Class dẫn xuất Hình Vuông
	/// </summary>
	class HinhVuong : HinhHoc       // Kế thừa
	{
		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai kế thừa từ lớp cha
			return (ChieuDai * 4);
		}

		public int TinhDienTich()
		{
			return (int)Math.Pow(ChieuDai, 2);
		}
	}
}
Kết quả chương trình

Kết quả chương trình

5. Hàm dựng Constructor trong kế thừa
5. Hàm dựng Constructor trong kế thừa

– Khi chúng ta tạo một đối tượng từ lớp con thì hàm dựng Constructor của lớp cha sẽ được gọi trước, được thực hiện trước, sau đó thì hàm dựng Constructor của lớp con mới được gọi thực thi. (Nôm na là phải có cha rồi mới có con Grin, nên hàm của cha sẽ được thực hiện trước.)

*** Ví dụ ③

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng hình chữ nhật
			HinhChuNhat hcn = new HinhChuNhat();

			Console.ReadKey();
		}
	}

	/// <summary>
	/// Class cơ sở Hình Học
	/// </summary>
	class HinhHoc
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public HinhHoc()
		{
			Console.WriteLine( "Constructor base class HinhHoc." );
		}
	}

	/// <summary>
	/// Class dẫn xuất Hình Chữ Nhật
	/// </summary>
	class HinhChuNhat : HinhHoc     // Kế thừa
	{
		/// <summary>
		/// Constructor
		/// </summary>
		public HinhChuNhat()
		{
			Console.WriteLine( "Constructor derived class HinhChuNhat." );
		}
	}
}

・ Kết quả chương trình:

Constructor base class HinhHoc.

Constructor derived class HinhChuNhat.

Trong C#, khi một class được tạo ra, nếu class đó chưa có Constructor, Visual C# sẽ ngầm định tự động generate một constructor mặc định (không tham số), vì nếu không làm vậy sẽ không thể tạo một object mới thuộc class đó rồi Grin, như các bạn thấy ở các ví dụ ① và ② chúng ta có thể tạo ra các đối tượng ConCun, HinhChuNhat, HinhVuong mà không cần tạo hàm Constructor.

Vậy trong trường hợp Constructor có tham số thì sẽ khai báo thế nào?

– Bây giờ chúng ta sẽ xét ví dụ sau có khai báo constructor có tham số ở lớp cha:

*** Ví dụ ④

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng hình chữ nhật, sử dụng constructor không tham số
			HinhChuNhat hcn = new HinhChuNhat();

			// Sử dụng phương thức kế thừa từ lớp cha
			hcn.setChieuRong(8);
			hcn.setChieuDai(9);

			// In Chu vi và Diện tích của hình chữ nhật
			Console.WriteLine("Chu vi hinh chu nhat: {0}", hcn.TinhChuVi());
			Console.WriteLine("Dien tich hinh chu nhat: {0}", hcn.TinhDienTich());

			Console.ReadKey();
		}
	}

	/// <summary>
	/// Class cơ sở Hình Học
	/// </summary>
	class HinhHoc
	{
		// Thuộc tính dùng chung
		protected int ChieuDai;

		protected int ChieuRong;

		/// <summary>
		/// Constructor không tham số
		/// </summary>
		public HinhHoc()
		{
			Console.WriteLine( "Constructor base class HinhHoc." );
		}

		/// <summary>
		/// Constructor có tham số
		/// </summary>
		/// <param name="cd">Chiều dài</param>
		/// <param name="cr">Chiều rộng</param>
		public HinhHoc(int cd, int cr)
		{
			ChieuDai = cd;
			ChieuRong = cr;
		}

		// Phương thức dùng chung
		public void setChieuDai(int inChieuDai)
		{
			ChieuDai = inChieuDai;
		}

		public void setChieuRong(int inChieuRong)
		{
			ChieuRong = inChieuRong;
		}
	}

	/// <summary>
	/// Class dẫn xuất Hình Chữ Nhật
	/// </summary>
	class HinhChuNhat : HinhHoc		// Kế thừa
	{
		/// <summary>
		/// Constructor không tham số
		/// </summary>
		public HinhChuNhat()
		{
			Console.WriteLine( "Constructor derived class HinhChuNhat." );
		}

		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai, ChieuRong kế thừa từ lớp cha
			return (ChieuDai + ChieuRong) * 2;
		}

		public int TinhDienTich()
		{
			return (ChieuDai * ChieuRong);
		}
	}
}

Đối với trường hợp ở lớp cha KHÔNG khai báo constructor có tham số thì việc khai báo constructor không tham số ở lớp cha có hay không cũng không sao cả. Chương trình vẫn chạy bình thường như ví dụ ① và ②.

Đối với trường hợp ở lớp cha CÓ khai báo constructor có tham số như ví dụ ④, thì sẽ có 2 trường hợp xảy ra:

Trường hợp 1: Lớp cha có khai báo constructor không tham số
Chương trình ở ví dụ ④ sẽ chạy được bình thường, vì khi tạo đối tượng hình chữ nhật, là chúng ta đang sử dụng constructor không tham số:

HinhChuNhat hcn = new HinhChuNhat();

Trường hợp 2: Lớp cha không khai báo constructor không tham số

– Nếu comment constructor không tham số ở lớp cha Hình học thì constructor không tham số ở lớp con Hình chữ nhật sẽ báo lỗi, vì lúc này nó không có constructor không tham số ở lớp cha base() để kế thừa.

*** Vì bản chất 2 cách viết bên dưới là tương đương nhau:

/// <summary>
/// Constructor
/// </summary>
public HinhChuNhat()
{
	Console.WriteLine( "Constructor derived class HinhChuNhat" );
}

Hoặc:

/// <summary>
/// Constructor
/// </summary>
public HinhChuNhat() : base()
{
	Console.WriteLine( "Constructor derived class HinhChuNhat" );
}
Comment hàm constructor không tham số ở lớp cha thì base() sẽ không còn tồn tại. Do đó constructor không tham số ở lớp con sẽ báo lỗi.

Comment hàm constructor không tham số ở lớp cha thì base() sẽ không còn tồn tại. Do đó constructor không tham số ở lớp con sẽ báo lỗi.

Do đó trong trường hợp ở lớp cha có khai báo constructor có tham số, mà không khai báo constructor không tham số thì ở lớp con bắt buộc phải khai báo một constructor có tham số.

– Chương trình ở ví dụ ④ sẽ được viết lại như sau:

using System;

namespace MinhHoangBlog
{
	class Program
	{
		static void Main(string[] args)
		{
			// Tạo đối tượng hình chữ nhật, sử dụng constructor có tham số
			HinhChuNhat hcn = new HinhChuNhat(8, 9);

			// In Chu vi và Diện tích của hình chữ nhật
			Console.WriteLine("Chu vi hinh chu nhat: {0}", hcn.TinhChuVi());
			Console.WriteLine("Dien tich hinh chu nhat: {0}", hcn.TinhDienTich());

			Console.ReadKey();
		}
	}

	/// <summary>
	/// Class cơ sở Hình Học
	/// </summary>
	class HinhHoc
	{
		// Thuộc tính dùng chung
		protected int ChieuDai;

		protected int ChieuRong;

		/// <summary>
		/// Constructor có tham số
		/// </summary>
		/// <param name="cd">Chiều dài</param>
		/// <param name="cr">Chiều rộng</param>
		public HinhHoc(int cd, int cr)
		{
			ChieuDai = cd;
			ChieuRong = cr;
		}
	}

	/// <summary>
	/// Class dẫn xuất Hình Chữ Nhật
	/// </summary>
	class HinhChuNhat : HinhHoc		// Kế thừa
	{
		/// <summary>
		/// Constructor có tham số
		/// </summary>
		public HinhChuNhat(int cd, int cr) : base(cd, cr)
		{
			// Tùy vào logic cụ thể Có thể có hoặc không có xử lý ở đây.
		}

		public int TinhChuVi()
		{
			// Sử dụng thuộc tính ChieuDai, ChieuRong kế thừa từ lớp cha
			return (ChieuDai + ChieuRong) * 2;
		}

		public int TinhDienTich()
		{
			return (ChieuDai * ChieuRong);
		}
	}
}
Kết quả chương trình

Kết quả chương trình

Như vậy, chúng ta đã vừa cùng nhau tìm hiểu xong tính kế thừa trong C#. Nếu có gì chưa hiểu thì các bạn comment nhé!

Cảm ơn bạn đã theo dõi. Đừng ngần ngại hãy cùng thảo luận với chúng tôi! Minh Hoàng Blog | Nào cùng vui hehe


Giới thiệu

Minh Hoàng

Xin chào, tôi là Hoàng Ngọc Minh, hiện đang làm BrSE, tại công ty Toyota, Nhật Bản. Những gì tôi viết trên blog này là những trải nghiệm thực tế tôi đã đúc rút ra được trong cuộc sống, quá trình học tập và làm việc. Các bài viết được biên tập một cách chi tiết, linh hoạt để giúp người đọc có thể tiếp cận một cách dễ dàng nhất. Hi vọng nó sẽ có ích hoặc mang lại một góc nhìn khác cho bạn[…]

Bình luận của bạn

2 Comments cho bài viết "Tính kế thừa trong lập trình C#"

avatar
Sắp xếp theo:   Mới nhất | Cũ nhất | Thích nhiều nhất
trackback

[…] Từ lớp dẫn xuất(lớp con) thành lớp cơ sở(lớp cha) (Khái niệm lớp dẫn xuất, lớp cơ sở được trình bày trong bài Tính kế thừa). […]

trackback

[…] – enum không được phép kế thừa. […]

wpDiscuz
Chúc bạn có một cuộc sống ngoại hạng!