Hiển thị các bài đăng có nhãn Lập trình hướng đối tượng. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Lập trình hướng đối tượng. Hiển thị tất cả bài đăng

Thứ Sáu, 17 tháng 3, 2017



Chào các bạn!

Hôm nay mình xin trở lại với chuỗi bài hướng dẫn về Lập trình hướng đối tượng OOP.  Trong C#, một class con có thể chứa các hàm cùng tên với các hàm trong class mà nó kế thừa. Điều đó là tốt đối với lập trình viên bởi họ có thể tận dụng điều đó để cài đặt mã nguồn của mình theo OOP, tuy nhiên nó đôi khi sẽ gây nhầm lẫn.
Bạn có thể định rõ ra cách mà hàm đó tương tác, hoạt động như thế nào bằng cách sử dụng từ khóa new hoặc override. 



Sự khác nhau được mô tả trong bài dưới đây:

Trong một chương trình Console, khai báo hai class: BaseClass và DerivedClass kế thừa từ BaseClass.


Trong hàm Main, khai báo các biến bc, dc, bcdc tương tướng với:
  • bc là đối tượng kiểu BaseClass, và mang giá trị kiểu BaseClass
  • dc là đối tượng kiểu DerivedClass, và mang giá trị kiểu DerivedClass
  • bcdc là đối tượng kiểu BaseClass và mang giá trị kiểu DerivedClass: Đây là biến cần chú ý tới nhiều nhất!
Bởi vì bc và bcdc điều có kiểu dữ liệu là BaseClass, nó chỉ nó thể truy cập trực tiếp đến Method1() (ngoại trừ khi bạn dùng casting ép kiểu nó về DerivedClass - việc này là có thể được).  Biến dc có thể truy cập đến cả Method1() và Method2(). Những mối quan hệ trên được thể hiện như hình bên dưới:


Ta thấy kết quả là những gì có thể đoán trước được. Tiếp tục ta thêm một hàm Method2() vào BaseClass. Bây giờ ta đã có hai hàm trùng tên, một nằm ở class cha và một nằm ở class con.


Bởi vì BaseClass bây giờ đã có Method2(), ta sẽ gọi được hàm Method2() ở các biến mạng kiểu là BaseClass là bc và bcdc:


Khi bạn build project, bạn sẽ thấy một thông báo cảnh cáo. Dòng warning nói rằng Method2() ở DerivedClass "che" đi Method2() ở BaseClass. Bạn cũng được khuyên nên sử dụng từ khóa new ở phần cài đặt/định nghĩa của Method2() nếu bạn muốn tạo ra kết quả tượng tự như vậy. Hoặc bạn có thể đổi tên Method2() để resolve warning đó, nhưng nó không phải lúc nào cũng tốt.

Trước khi thêm từ khóa new, chạy chương trình và xem output tạo ra bởi những hàm đã gọi, bạn sẽ nhận được như sau:


Hãy chú ý đến những dòng được mình highlight, dòng thứ 2 được bc mang kiểu BaseClass nên việc nó chỉ gọi hàm Method2() của lớp cha cũng không có gì là lạ. Ở hai dòng cuối, hành vi mặc định của nó được giữ nguyên, nên nó ưu tiên gọi hàm từ lớp cha dù ở lớp con cũng có một hàm Method2() cùng tên.
Từ khóa new "bảo tồn" mối quan hệ mà tạo ra kết quả ban đầu, nhưng nó vô hiệu hóa đi các cảnh bảo từ IDE. Các biến có kiểu là BaseClass vẫn giữ nguyên hành vi của nó và tiếp tục truy cập các hàm/thuộc tính của BaseClass, và các biến có kiểu là DerivedClass tiếp tục tuy cập vào các hàm/thuộc tính của DerivedClass đầu tiên, sau đó mới suy xét đến những thành phần mà nó kế thừa từ BaseClass.

Ví dụ: 
Ta có thể thêm từ khóa new trước hoặc sau public

Chạy lại chương trình để chắc rằng output không thay đổi. Cũng để thấy rằng những warnings từ IDE không còn xuất hiện nữa.
Bằng cách sử dụng từ khóa new, bạn thầm khẳng định rằng bạn biết hàm/thuộc tính đó "che" đi hàm/thuộc tính mà nó kế thừa từ chính lớp cha của nó. Để biết thêm thông tin về ẩn tên (name hiding) trong kế thừa, xem new Modifier

Mặc khác, để tạo ra 1 hành vi tương phản lại ở trên, ta sử dụng từ khóa override. Thêm hàm sau kèm từ khóa override vào DerivedClass và từ khóa virtual vào tên hàm ở BaseClass
Từ khóa override có thể đặt trước hoặc sau public


Chạy lại chương trình, ta chú ý sự khác nhau giữa 2 dòng cuối của output với nhau, và so với output của lần test trước. Qua đó thấy rõ sự khác nhau của override và new.


Ta thấy khi gọi hàm Method1() trùng tên giữa 2 lớp, mang từ khóa override từ biến bcdc lần này nó không gọi hàm của lớp cha nữa, mà chuyển sang gọi hàm của lớp con. Tuy nhiên cũng là gọi hàm trùng tên Method2(), không có từ khóa override thì nó sẽ trở lại hành vi mặc định là gọi hàm từ lớp cha.

Việc sử dụng từ khóa override làm cho biến bcdc có thể truy cập đến Method1() của lớp con DerivedClass. Thông thường, đây mới là hành vi được các lập trình viên chúng ta mong muốn khi sử dụng OOP và kế thừa. Bạn luôn muốn những object được khởi tạo như một thể hiện của lớp con sử dụng các hàm từ chính lớp con đó chứ không phải những hàm cùng tên, được cài đặt từ trước ở lớp cha. 

Tuy nhiên hành động mặc định của C# lại ưu tiên lớp cha hơn :D. Vậy nên bạn có thể đạt được mong muốn của mình bằng cách phải dùng từ khóa override để extend và ghi đè những hàm của lớp cha bằng những hàm mới cùng tện trong lớp con.

Full source code:


Đến đây các bạn đã hiểu về sự khác nhau giữa new và override trong C# chưa nào? 
Bạn có thể tham khảo thêm ví dụ sau đây để biết rõ hơn về chúng. Mọi thắc mắc xin để lại bình luận bên dưới nhé.

Chào các bạn, chúc các bạn học tốt!

Thứ Hai, 19 tháng 12, 2016


Chào các bạn,

Đã lâu rồi mình không viết bài về C# nhỉ :D, hôm nay nhân lúc đang làm đồ án môn Thiết kế phần mềm, cần sử dụng OOP, inheritance,.. khá nhiều, mình xin viết một article nho nhỏ về hàm dựng (constructor) trong tính kế thừa của C#.



Theo nguyên tắc, khi khởi tạo đối tượng của lớp con, thì hàm dựng (constructor) của lớp cha sẽ được gọi trước, sau đó mới tới đổi tượng của lớp con.

Ví dụ mình có class Base và Child kế thừa từ lớp Base như sau:


Thì khi Run, chương trình sẽ cho kết quả như sau:


Rõ ràng constructor của lớp Base đã được gọi trước! 

Chúng ta có thể kiểm tra các trường hợp khác:
 - Cho lớp Base thành abstract.
 - Sửa lại hàm Main, khởi tạo đối tượng bằng Child c = new Child();

Tất cả đều cho kết quả tương tự.

Chúng ta tiếp tục thử một trường hợp đặc biệt khác, thêm một constructor mới chứa tham số vào class Base và Child như sau:


Run chương trình, chúng ta được kết quả sau:


Ở đây bạn thấy, dù chúng ta tạo đối tượng với constructor có tham số, thì constructor cơ bản của lớp Base luôn được gọi.
Bây giờ nếu bạn xóa đi constructor cơ bản (không tham số) của lớp Base và Run chương trình thì sẽ bị lỗi ngay lập tức.


Từ đó ta rút ra các kết luận sau:

Về vấn đề lỗi khi xóa constructor cơ bản của lớp Base: Khi một class được tạo ra, nếu class đó chưa có constructor, Visual C# sẽ tự động generate một constructor mặc định (không tham số) cho chúng ta, vì nếu không làm vậy chúng ta sẽ không thể tạo một object mới thuộc class đó rồi :D. Tuy nhiên nếu bạn tạo thêm một constructor khác có tham số, và xóa constructor mặc định cơ bản đi, thì Visual C# sẽ cho rằng class của bạn đã có thể tự tạo một object mới và sẽ không generate constructor mặc định. Điều đó đồng nghĩa với việc các object được tạo bằng cách gọi "new Base()" sẽ không thể khởi tạo.

Về vấn đề chính về constructor trong kế thừa mình muốn nói là khi bạn khởi tạo một đối tượng lớp con Child, thì C# sẽ automatically gọi constructor mặc định-không tham số của lớp cha Base trước, sau đó mới gọi constructor của lớp con (constructor nào của lớp con thì còn tùy mình new đối tượng với tham số gì :D).


Tại sao lại như vậy?

Theo ý kiến chủ quan của cá nhân mình, thì bạn cứ tưởng tượng lớp con Child là một "cụ thể hóa" của một lớp cha, giống như một chiếc xe, trước khi thành một chiếc xe nó phải qua công đoạn lắp ráp khung xe, thì KhungXe đó chính là lớp cha. Mà để tạo khung xe thì phải dùng constructor của KhungXe, trong đó sẽ gọi các hàm ráp, hàn, bắt ốc,... Sau khi đã xong phần khung xe, ta gọi constructor của XeMay gọi các hàm gắn bánh, gắn yên xe,.. giúp tạo thành một chiếc xe hoàn chỉnh.
Vậy thứ tự gọi thực sự logic và thực tế đúng không nào?

Nếu bạn muốn gọi constructor có tham số cụ thể của lớp cha theo ý của mình thì sử dụng tự khóa "base" như sau:



Trên là một số kiến thức tổng quát về Lập trình hướng đối tượng (OOP) cho các bạn nào lỡ quên :D.
Minh xin nói luôn một cách tổng quát, trong Java thì hàm dựng của lớp cha cũng sẽ tự động được gọi khi khởi tạo đối tượng của lớp con. Các bạn có thể tham khảo tại đây.



Cuối cùng, mình có một câu hỏi cho các bạn, có cách nào để tạo một đối tượng thuộc lớp con mà không cần gọi hàm dựng (constructor) của lớp cha không? 

Chào các bạn, chúc các bạn học tốt!


Categories

Sample Text

Được tạo bởi Blogger.

Must Read

Biểu mẫu liên hệ

Tên

Email *

Thông báo *

Popular Posts

Video

Popular Posts

Our Facebook Page