Trong ví dụ 3, phương thức DrawWindow() của lớp ListBox sẽ làm ẩn và thay thế phương thức DrawWindow của lớp cơ sở Window. Khi chúng ta gọi phương thức DrawWindow của một đối tượng của lớp ListBox thì phương thức ListBox.DrawWindow() sẽ được thực hiện, không phải phương thức Window.DrawWindow() của lớp cơ sở Window. Tuy nhiên, ta có thể gọi phương thức DrawWindow() của lớp cơ sở thông qua từ khóa base:
base.DrawWindow(); // gọi phương thức cơ sở.
Từ khóa base chỉ đến lớp cơ sở cho đối tượng hiện hành.
Gốc của tất cả các lớp: Lớp Object
Tất cả các lớp của ngôn ngữ C# của bất cứ kiểu dữ liệu nào thì cũng được dẫn xuất từ lớp System.Object. Thú vị là bao gồm cả các kiểu dữ liệu giá trị. Một lớp cơ sở là cha trực tiếp của một lớp dẫn xuất. Lớp dẫn xuất này cũng có thể làm cơ sở cho các lớp dẫn xuất xa hơn nữa, việc dẫn xuất này sẽ tạo ra một cây thừa kế hay một kiến trúc phân cấp. Lớp gốc là lớp nằm ở trên cùng cây phân cấp thừa kế, còn các lớp dẫn xuất thì nằm bên dưới. Trong ngôn ngữ C#, lớp gốc là lớp Object, lớp này nằm trên cùng trong cây phân cấp các lớp.
Lớp Object cung cấp một số các phương thức dùng cho các lớp dẫn xuất có thể thực hiện việc phủ quyết. Những phương thức này bao gồm: Equals() kiểm tra xem hai đối tượng có giống nhau hay không. Phương thức GetType() trả về kiểu của đối tượng. Và phương thức ToString () trả về một chuỗi thể hiện lớp hiện hành.
Chức năng của một số phương thức
GetHashCode( ): Cho phép những đối tượng cung cấp riêng những hàm băm cho sử dụng tập hợp.
GetType( ): Cung cấp kiểu của đối tượng.
ToString( ): Cung cấp chuỗi thể hiện của đối tượng. Finalize( ): Dọn dẹp các tài nguyên.
MemberwiseClone( ): Tạo một bản sao từ đối tượng.
Ví dụ 5 sau minh họa việc sử dụng phương thức ToString( ) thừa kế từ lớp Object.
Ví dụ 5: Thừa kế từ Object.
---
using System;
public class SomeClass {
public SomeClass( int val )
{
value = val; }
// phủ quyết phương thức ToString của lớp Object
public virtual string ToString()
{
return value.ToString();
}
// biến thành viên private lưu giá trị
private int value; }
public class Tester {
static void Main( )
{
int i = 5;
Console.WriteLine(“The value of i is: {0}”, i.ToString()); SomeClass s = new SomeClass(7);
Console.WriteLine(“The value of s is {0}”, s.ToString()); Console.WriteLine(“The value of 5 is {0}”,5.ToString()); }
Kết quả:
The value of i is: 5 The value of s is 7
The value of 5 is 5
---
Trong tài liệu của lớp Object phương thức ToString() được khai báo như sau:
public virtual string ToString();
Đây là phương thức ảo public, phương thức này trả về một chuỗi và không nhận tham số. Tất cả kiểu dữ liệu được xây dựng sẵn, như kiểu int, dẫn xuất từ lớp Object nên nó cũng có thể thực thi các phương thức của lớp Object.
Lớp SomeClass trong ví dụ trên thực hiện việc phủ quyết phương thức ToString(), do đó phương thức này sẽ trả về giá trị có nghĩa. Nếu chúng ta không phủ quyết phương thức ToString() trong lớp SomeClass, phương thức của lớp cơ sở sẽ được thực thi, và kết quả xuất ra sẽ có thay đổi như sau:
The value of s is SomeClass
Như chúng ta thấy, hành vi mặc định đã trả về một chuỗi chính là tên của lớp đang thể hiện. Các lớp không cần phải khai báo tường minh việc dẫn xuất từ lớp Object, việc kế thừa sẽ được đưa vào một cách ngầm định. Như lớp SomeClass trên ta không khai báo bất cứ dẫn xuất của lớp nào nhưng C# sẽ tự động đưa lớp Object thành lớp dẫn xuất. Do đó ta mới có thể phủ quyết phương thức ToString() của lớp Object.
Phương thức Overriden và Hide:
Bằng cách khai báo virtual trong một hàm ở lớp cơ sở thì cho phép hàm đó được overriden trong bất kỳ một lớp thừa hưởng nào.
class MyBaseClass {
public virtual string VirtualMethod()
{
return "This method is virtual and defined in MyBaseClass"; }
Như ví dụ trên, tức là ta có thể tạo ra một sự thực thi khác của
VirtualMethod() trong một lớp thừa hưởng của MyBaseClass. Và khi gọi phương thức trong một thể hiện của lớp thừa hưởng thì phương thức của lớp thừa hưởng sẽ được thi hành mà không quan tâm đến phương thức đó ở lớp cơ sở. Khác với C++ và Java, trong C# những hàm không được khai báo virtual
mặc định mà bạn phải khai báo virtual một cách rõ ràng và khi một hàm muốn override một hàm khác thì phải sử dụng từ khoá override:
class MyDerivedClass : MyBaseClass
{
public override string VirtualMethod()
{
return "This method is an override defined in MyDerivedClass";
} }
Những trường thành viên(member fields) và những hàm tĩnh thì không được khai báo Virtual. Nếu một phương thức có cùng đặc tính trong cả hai khai báo ở lớp cơ sở và lớp thừa hưởng nhưng các phương thức này thì không khai báo virtual hay overriden thì sẽ được gọi là: "lớp thừa hưởng hide lớp cơ sở đó". Kết quả là: phương thức nào được gọi phụ thuộc vào kiểu của biến được sử dụng để tham khảo đến thể hiện, chứ không phải kiểu của chính thể hiện đó.
Trong hầu hết mọi trường hợp bạn luôn thích override một phương thức hơn là hide nó. Bởi vì hide dễ gây ra những lỗi nghiêm trọng. Nhưng cú pháp C# được thiết kế để bảo đảm cho những nhà phát triển sẽ được cảnh báo trong thời gian biên dịch về vấn đề nghiêm trọng này. Nếu bạn tạo ra hai phương thức hoàn toàn giống nhau ở cả lớp thừa hưởng và lớp cơ sở mà không có khai báo virtual và override thì bạn sẽ bị cảnh báo trong khi biên dịch. Trong C#, bạn nên sử dụng từ khoá new để đảm bảo bạn muốn hide phương thức đó.
Ví dụ 5: Sử dụng phương thức ảo.
---
using System;
public class Window {
public Window( int top, int left ) {
this.top = top; this.left = left; }
// phương thức được khai báo ảo
public virtual void DrawWindow()
{
Console.WriteLine( “Window: drawing window at {0}, {1}”, top, left ); }
// biến thành viên của lớp
protected int top; protected int left; }
public class ListBox : Window
{
// phương thức khởi dựng có tham số
public ListBox( int top, int left, string contents ): base( top, left) {
listBoxContents = contents; }
// thực hiện việc phủ quyết phương thức DrawWindow
public override void DrawWindow()
{
base.DrawWindow();
Console.WriteLine(“ Writing string to the listbox: {0}”, listBoxContents);
}
// phủ quyết phương thức DrawWindow của lớp cơ sở
public override void DrawWindow()
{
Console.WriteLine(“ Drawing a button at {0}: {1}”, top, left); }
}
public class Tester {
static void Main()
{
Window win = new Window(1,2);
ListBox lb = new ListBox( 3, 4, “ Stand alone list box”); Button b = new Button( 5, 6 );
win.DrawWindow(); lb.DrawWindow(); b.DrawWindow();
winArray[0] = new Window( 1, 2 );
winArray[1] = new ListBox( 3, 4, “List box is array”); winArray[2] = new Button( 5, 6 );
for( int i = 0; i < 3; i++) { winArray[i].DrawWindow(); } } } --- Kết quả:
Window: drawing window at 1: 2 Window: drawing window at 3: 4
Writing string to the listbox: Stand alone list box Drawing a button at 5: 6
Window: drawing Window at 1: 2 Window: drawing window at 3: 4
Writing string to the listbox: List box is array Drawing a button at 5: 6
Lời kết
Chương này đã giới thiệu đến các bạn cấu trúc của một lớp - thành phần làm nên một chương trình hướng đối tượng - và lập trình hướng đối tượng trong C#. Trong chương này chúng ta đã thảo luận về nội dung cơ bản của lập trình hướng đối tượng nói chung và cách thể hiện nội dung của một lớp là các trường, thuộc tính, phương thức và sự kiện trong C# nói riêng. Ngoài ra nội dung chương còn đề cập đến nhiều vấn đề khác nữa như việc thực hiện kế thừa và nạp chồng… trong một chương trình C#.
Chúng em đã cố gắng hoàn thành chương này với mục đích giúp người đọc có cái nhìn toàn diện về phương pháp lập trình hướng đối tượng và thực hiện nó trên ngôn ngữ lập trình C#. Điều tất yếu là có nhưng sai lầm và thiếu xót. Chúng em mong có được sự góp ý từ thầy cô và các bạn để hoàn thiện sản phẩm của mình.
BÀI TẬP
1. Xây dựng lớp thời gian Time. Dữ liệu thành phần bao gồm giờ, phút giây. Các hàm thành phần bao gồm: hàm tạo, hàm truy cập dữ liệu, hàm normalize() để chuẩn hóa dữ liệu nằm trong khoảng quy định của giờ (0 ≤ giờ < 24) , phút (0 ≤ phút <60), giây (0 ≤ giây <60), hàm advance(int h, int m, int s) để tăng thời gian hiện hành của đối tượng đang tồn tại, hàm reset(int h, int m, int s) để chỉnh lại thời gian hiện hành của một đối tượng đang tồn tại và một hàm print() để hiển thị dữ liệu.
2. Xây dựng lớp Date. Dữ liệu thành phần bao gồm ngày, tháng, năm. Các hàm thành phần bao gồm: hàm tạo, hàm truy cập dữ liệu, hàm normalize() để chuẩn hóa dữ liệu nằm trong khoảng quy định của ngày (1 ≤ ngày <daysIn(tháng)), tháng (1 ≤ tháng < 12), năm (năm ≥ 1), hàm daysIn(int) trả về số ngày trong tháng, hàm advance(int y, int m, int d) để tăng ngày hiện lên các năm y, tháng m, ngày d của đối tượng đang tồn tại, hàm reset(int y, int m, int d) để đặt lại ngày cho một đối tượng đang tồn tại và một hàm print() để hiển thị dữ liệu.
3. Thực hiện một lớp String. Mỗi đối tượng của lớp sẽ đại diện một chuỗi ký tự. Những thành phần dữ liệu là chiều dài chuỗi, và chuỗi ký tự. Các hàm thành phần bao gồm: hàm tạo, hàm truy cập, hàm hiển thị, hàm character(int i)trả về một ký tự trong chuỗi được chỉ định bằng tham số i.
4. Xây dựng lớp ma trận có tên là Matrix cho các ma trận, các hàm thành phần bao gồm: hàm tạo mặc định, hàm nhập xuất ma trận, cộng, trừ, nhân hai ma trận.
5. Xây dựng lớp ma trận có tên là Matrix cho các ma trận vuông, các hàm thành phần bao gồm: hàm tạo mặc định, hàm nhập xuất ma trận, tính định thức và tính ma trận nghịch đảo.
6. Xây dựng lớp Stack cho ngăn xếp kiểu int. Các hàm thành phần bao gồm: Hàm tạo mặc định, hàm hủy, hàm isEmpty() kiểm tra stack có rỗng không, hàm isFull() kiểm tra stack có đầy không, hàm push() , pop(), hàm in nội dung ngăn xếp. Sử dụng một mảng để thực hiện.
7. Xây dựng lớp hàng đợi Queue chứa phần tử kiểu int. Các hàm thành phần bao gồm: hàm tạo, hàm hủy và những toán tử hàng đợi thông thường: hàm insert() để thêm phần tử vào hàng đợi, hàm remove() để loại bỏ phần tử, hàm isEmpty() kiểm tra hàng đợi có rỗng
8. Xây dựng lớp Sinhvien để quản lý họ tên sinh viên, năm sinh, điểm thi 9 môn học của các sinh viên. Cho biết sinh viên nào được làm khóa luận tốt nghiệp, bao nhiêu sinh viên thi tốt nghiệp, bao nhiêu sinh viên thi lại, tên môn thi lại. Tiêu chuẩn để xét như sau:
- Sinh viên làm khóa luận phải có điểm trung bình từ 7 trở lên, trong đó không có môn nào dưới 5.
- Sinh viên thi tốt nghiệp khi điểm trung bình nhỏ hơn 7 và điểm các môn không dưới 5. - Sinh viên thi lại môn dưới 5.
9. Xây dựng lớp vector để lưu trữ các vectơ gồm các số thực. Các thành phần dữ liệu bao gồm:
- Kích thước vectơ.
- Một mảng động chứa các thành phần của vectơ.
Các hàm thành phần bao gồm hàm tạo, hàm hủy, hàm tính tích vô hướng hai vectơ, tính chuẩn của vectơ (theo chuẩn bất kỳ nào đó).
10. Xây dựng lớp Phanso với dữ liệu thành phần là tử và mẫu số. Các hàm thành phần bao gồm:
- Cộng hai phân số, kết quả phải dược tối giản - Trừ hai phân số, kết quả phải dược tối giản - Nhân hai phân số, kết quả phải dược tối giản - Chia hai phân số, kết quả phải dược tối giản
9. Định nghĩa các phép toán tải bội trên các lớp Time, Date, ma trận, đa thức, phân số
10. Xây dựng lớp có tên là Stack với các thao tác cần thiết. Từ đó hãy dẫn xuất từ lớp stack để đổi một số nguyên dương sang hệ đếm bất kỳ.
11. Viết một phân cấp kế thừa cho các lớp hình tứ giác, hình thang, hình bình hành, hình chữ nhật, hình vuông.
12. Tạo một lớp cơ sở có tên là airship để lưu thông tin về số lượng hành khách tối đa và trọng lượng hàng hóa tối đa mà máy bay có thể chở được. Từ đó hãy tạo hai lớp dẫn xuất airplane và balloon, lớp airplane lưu thông tin về kiểu động cơ (gồm động cơ cánh quạt và
13. Một nhà xuất bản nhận xuất bản sách. Sách có hai loại: loại có hình ảnh ở trang bìa và loại không có hình ảnh ở trang bìa. Loại có hình ảnh ở trang bìa thì phải thuê họa sĩ vẽ bìa. Viết chương trình thực hiện các yêu cầu :
- Tạo một lớp cơ sở có tên là SACH để lưu thông tin về tên sách, tác giả, số trang, giá bán và định nghĩa hàm thành phần cho phép nhập dữ liệu cho các đối tượng của lớp SACH. - Tạo lớp BIA kế thừa từ lớp SACH để lưu các thông tin : Mã hình ảnh, tiền vẽ và định nghĩa hàm thành phần cho phép nhập dữ liệu cho các đối tượng của lớp BIA.
- Tạo lớp HOASY để lưu các thông tin họ tên, địa chỉ của họa sỹ và định nghĩa hàm thành phần cho phép nhập dữ liệu cho các đối tượng của lớp HOASY.
- Tạo lớp SACHVEBIA kế thừa từ lớp BIA và lớp HOASY và định nghĩa hàm thành phần cho phép nhập dữ liệu cho các đối tượng của lớp SACHVEBIA. Viết hàm main() cho phép nhập vào hai danh sách : danh sách các sách có vẽ bìa và danh sách các sách không có vẽ bìa (có thể dùng mảng tĩnh hoặc mảng con trỏ).
14. Xây dựng lớp hình vuông có tên là HVUONG với các dữ liệu thành phần như sau: độ dài cạnh. Các hàm thành phần để nhập dữ liệu, hiển thị dữ liệu, tính diện tích, chu vi hình vuông. Từ lớp HVUONG, xây dựng lớp dẫn xuất có tên là CHUNHAT, là lớp kế thừa của lớp HVUONG và được bổ sung thêm thuộc tính sau: độ dài cạnh thứ hai. Các hàm thành phần để nhập dữ liệu, hiển thị dữ liệu để tính diện tích và chu vi hình chữ nhật. Viết chương trình minh họa.
15. Xây dựng lớp cơ sở CANBO có dữ liệu thành phần là mã cán bộ, mã đơn vị, họ tên, ngày sinh. Các hàm thành phần bao gồm: nhập dữ liệu cán bộ, hiển thị dữ liệu. Lớp dẫn xuất LUONG kế thừa lớp CANBO và có thêm các thuộc tính: phụ cấp, hệ số lương, bảo hiểm. Hàm thành phần để tính lương cán bộ theo công thức:
lương = hệ số lương *290000 + phụ cấp - bảo hiểm Hãy thiết kế chương trình để đáp ứng các yêu cầu:
- Nhập danh sách cán bộ
- In bảng lương các cán bộ theo từng đơn vị.