Upload
missthom
View
473
Download
1
Embed Size (px)
Citation preview
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 1
Hướng dẫn học môn Nguyên lý lập trình 2 và Thực hành nguyên lý lập trình 2
Đánh giá: Thi máy giữa kì(30% điểm) cho môn nguyên lý lập trình 2. Thi viết cuối kì(70% điểm) cho môn nguyên lý lập trình 2. Thi máy cuối kì(100% điểm) cho môn thực hành nguyên lý lập trình 2. Có thêm các bài kiểm tra trong quá trình thực hành và cộng điểm vào bài thi giữa kì hoặc
cuối kì. Tài liệu tham khảo
Bài giảng môn nguyên lý lập trình 2. Download eBook “Wrox Press - Professional C#, 3rd Edition.pdf ” tại đường dẫn sau
\\192.168.6.1\cisco\Downloads. MSDN-Microsoft Development Networks Website: http://www.codeproject.com. Một số sourcecode mẫu được cung cấp trong quá trình học.
Thực hành Bao gồm 5 bài lab. Download bài lab tại địa chỉ http://192.168.6.1/NLLT2/labX.htm. Trong đó X là thứ tự bài
lab. Ngôn ngữ minh họa cho lập trình hướng đối tượng: C#(C Sharp).
Môi trường thực hành Visual Studio 2003 hoặc 2005.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 2
PHẦN 1: NGÔN NGỮ C# 1. C# và .Net
1.1 Nền tảng .Net
Khung .Net là một nền tảng phát triển ứng dụng mới cung cấp bộ giao tiếp lập trình ứng
dụng cho các dịch vụ và các lớp API của hệ điều hành Windows. Chúng hỗ trợ COM+, phát triển
ứng dụng Web, XML, thiết kế hướng đối tượng và hỗ trợ các giao thức dịch vụ Web mới như
SOAP, WSDL, UDDI.
1.2 Khung .Net
.Net không chỉ hỗ trợ độc lập ngôn ngữ mà còn tích hợp nhiều ngữ nghĩa khác nhau. Điều
này cho phép chúng ta có thể thực hiện kế thừa từ lớp, xử lý biệt lệ và tính năng đa hình theo
nhiều ngữ nghĩa khác nhau. .Net một cung một đặc tả gọi là Common System Type(CTS) mà tất
cả các thành phần .Net phải tuân theo. CTS hỗ trợ các khái niệm tổng quát của lớp, giao tiếp, (ủy
nhiệm)delegate, kiểu tham chiếu và kiểu giá trị.
Ngoài ra, .Net còn cung cấp một Common Language Specification (CLS) cung cấp các
luật cơ bản để đáp ứng yêu cầu tích hợp ngôn ngữ giữa các ngôn ngữ lập trình và hệ thống khác
nhau. Hình sau biểu diễn cấu trúc của .Net
Hình 1.1: Kiến trúc khung .Net.
1.3 Biên dịch
Điểm tập trung của nền tảng .Net là môi trường thực hiện việc thực thi ứng dụng gọi là
CLR(Commong Language Runtime-CLR)
Trong .Net chương trình không biên dịch thành tập tin thực thi, chúng được biên dịch theo
hai bước:
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 3
Biên dịch mã nguồn thành IL(Intermediate Language).
Dùng CLR để biên dịch IL thành mã máy theo từng nền tảng thích hợp.
Việc thực hiện như trên cung cấp nhiều thuận lợi cho .Net như:
Độc lập nền tản và phần cứng
Nâng cao hiệu suất
Giúp cho các ngôn ngữ phát triển trên các ngôn ngữ lập trình khác nhau có thể tương tác
với nhau.
Một số đặc trưng quan trọng của IL:
Hướng đối tượng và sử dụng interface
Phân biệt rõ ràng giữa kiểu tham biến và tham trị.
Hổ trợ nhiều kiểu dữ liệu khác nhau
Quản lý lỗi thông qua xử lý biệt lệ
Các kiểu trong .Net
1.4 Ngôn ngữ C#
Ngôn ngữ C# hỗ trợ cấu trúc, dựa trên thành phần, OOP và các khái niệm trong các ngôn
ngữ lập trình hiện đại. Đặc trưng và khái niệm quan trọng nhất của OOP là hỗ trợ định nghĩa và
làm việc với lớp. Lớp định nghĩa các kiểu mới cho phép chúng ta mở rộng ngôn ngữ theo những
mô hình tốt hơn nhằm giải quyết vấn đề chúng ta đang gặp phải.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 4
Assembly là một tập các tập tin cung các thư viện liên kết động cho người dùng.
Trong .Net, assembly là một đơn vị cơ bản cho việc tái sử dụng mã, bảo mật, phát triển và xác
định phiên bản.
Hỗ trợ truy xuất bộ nhớ trực tiếp tương tự như khái niệm con trỏ trong c++.
2. Tạo ứng dụng trong C#
Đầ tiên chúng ta viêt một chương trình ứng dụng “Hello World” đơn giản sử dụng C#:
class HelloWorld { static void Main( ) { System.Console.WriteLine("Chuong Trinh Dau Tien");
System.Console.Readline(); } }
2.1 Lớp, đối tượng và kiểu
Điều cần thiết trong ngôn ngữ lập trình hướng đối tượng là tạo các kiểu dữ liệu mới. Các
kiểu biểu diễn cho một cái gì đó có thể là trừu tượng(bảng dữ liệu, luồng) hay rõ ràng(cửa sổ, nút
lệnh). Một kiểu định nghĩa các đặc trưng và hành vi cho một cái gì đó. Tương tự các ngôn ngữ
lập trình hướng đối tượng khác, trong C# một kiểu được gọi là một lớp(Class). Trong khi các thể
hiện của các cá thể gọi là đối tượng(object).
Chưong trình trên khai báo một kiểu đơn giản: lớp HelloWorld sử dụng từ khóa class và
tên “HelloWorld” .Sau đó là định nghĩa thuộc tính và hành vi của lớp. Định nghĩa thuộc tính và
hành vi của một lớp trong C# phải được bao bởi {}.
2.1.1 Phương thức
Mỗi lớp có thuộc tính và hành vi. Hành vi được định nghĩa là phương thức thành viên của
lớp. Một phương thức là một hàm sở hữu bởi lớp mà chúng ta định nghĩa. Thỉnh thoảng người ta
gọi phương thức thành viên là hàm thành viên. Hàm thành viên định nghĩa những gì lớp của
chúng ta có thể làm và hành vi của nó như thế nào? Thông thường một phương thức là một tên
của hành động ví dụ WriteLine. Tuy nhiên chúng cũng có thể là các tên đặc biệt như Main().
Khai báo phương thức là một liên lạc giữa người tạo và sử dụng phương thức. Để khai báo
một phương thức chúng ta cần chỉ ra kiểu giá trị trả về theo sau bởi tên. Một phương thức có thể
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 5
có tham số hay khônng. Ví dụ sau là một hàm với một tham số kiểu nguyên và phương thức trả
về một giá trị kiểu nguyên.
int TinhTong(int n);
Nếu hàm không trả về giá trị nó sẽ trả về kiểu void
void HienThi( );
2.1.2 Chú thích
Chú thích sử dụng trong C# tương tự như trong C++. Dùng // chú thích cho dòng và /* */
cho một đoạn. Ví dụ sau minh họa sử dụng chú thích trong C#.
class Hello
{
static void Main( )
{
/* Chương trình hiển thị một chuỗi ra màn hình */
System.Console.WriteLine("Chuong Trinh Dau Tien");
}
}
2.1.3 Ứng dụng dòng lệnh (Console)
Ứng dụng dòng lệnh là ứng dụng không có giao diện người dùng. Việc xuất nhập thông
qua dòng lệnh chuẩn. Phương thức Main() trong ví dụ “Hello World” viết chuỗi “Chuong Trinh
Dau Tien” lên màn hình. Màn hình được quản lý bởi một đối tượng tên Console. Đối tượng này
có một phương thức WriteLine(), nhận một chuỗi và ghi chúng ra thiết bị xuất chuẩn(màn hình).
2.1.4 Không gian tên
Console là một phần của FCL. Mỗi lớp có một tên và do đó FCL chứa hàng ngàn tên như
ArrayList, HashTable… C# cung cấp một không gian tên cho việc quản lý các tên này. Trong ví
dụ trên đối tượng Console chỉ hạn chế trong không gian tên System được sử dụng theo đoạn mã
sau:
System.Console.WriteLine();
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 6
2.1.5 Toán tử “.”
Trong ví dụ trên toán tử “.”được sử dụng để truy cập phương thức và dữ liệu trong
lớp(phương thức WriteLine()) và hạn chế tên lớp trong một không gian tên đặc biệt( vị trí
Console trong không gian tên System).
Trong nhiều trường hợp, không gian tên được chia thành các không gian con. Ví dụ không
gian tên System chứa một số các không gian tên con như Configuration, Collections...trong khi
không gian tên Collections lại chứa các không gian tên con khác.
2.1.6 Từ khóa using
Trong ví dụ 2-1 chúng ta sử dụng từ System trước Console chúng ta có thể dùng từ khóa
using để chỉ ta chúng ta sẽ sử dụng các kiểu trong không gian tên System bằng cách ghi:
using System.
Ví dụ sử dụng từ khoá using như sau
using System; class Hello { static void Main( ) { Console.WriteLine("Chuong Trinh dau Tien"); } }
2.1.7 Quy ước đặt tên
C# phân biệt chữ hoa và chữ thường. Nghĩa là khi chúng ta viết WriteLine() thí khác
writeLine(). Do đó chúng ta cần có quy ước cách đặt tên biến, hằng, hàm..
2.1.8 Từ khóa static
Phương thức Main() chỉ ra trong ví dụ 2-1 có nhiều chức năng khác. Trước từ khóa void
chúng ta thấy từ khóa static.
static void Main()
Từ khóa static chỉ ra rằng chúng ta có thể gọi Main() mà không cần tạo đối tượng kiểu
Hello. Chúng ta sử dụng từ khóa này khi muốn gọi một phương thức trong lớp mà không cần tạo
đối tượng.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 7
2.2 Cách chạy chương trình “Hello world”
Để thực hiện được chương trình chúng ta sử dụng Visual Studio .Net Intergated
Development Environment (IDE) trong công cụ Visual Studio.Net. IDE cung cấp những công cụ
rất mạnh cho việc dò lỗi và công cụ hỗ trợ khác.
2.2.1 Soạn thảo chương trình “Hello Wolrd”
• Chạy chương trình IDE. Chọn Visual Studio .Net từ thực đơn Start
• Chọn File New Project. Chọn kiểu dự án là Visual C# Project và dạng Console
Application như trong hình 2-1. Chúng ta có thể nhập vào tên dự án và đường dẫn để lưu
trữ dự án. Sau khi chọn nút OK, một cửa sổ mới sẽ xuất hiện như hình 2.2
Hình 2-1:Tạo ứng dụng dòng lệnh trong Visual Studio.Net
Hình 2.2: Cửa sổ soạn thảo cho một dự án mới
• Sau đó đưa lệnh sau vào trong hàm Main()
System.Console.WriteLine("Chuong Trinh Dau Tien");
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 8
2.2.2 Biên dịch và chạy chương trình “Hello Wolrd”
Có nhiều cách để biên dịch và chạy chương trình trong Visual Studio .Net
• Chọn Ctl+Shift+B hay Build build từ thực đơn
• Chọn nút Build như trong hình 2-3
Hình 2-3: Nút build
Để chạy chương trình mà không thực hiện dò lỗi:
• Nhấn Ctrl + F5 hay Debug Start Without Debugging từ thực đơn.
• Chọn nút Start Without Debugging như trong hình 2-4
Hình 2-4: Nút Start Without Debugging
2.3 Sử dụng công cụ dò lỗi của Visual Studio .Net
3 kỹ năng quan trọng khi dò lỗi:
• Bằng cách nào đặt các điểm dừng(breakpoint) và chạy các điểm dùng như thế nào?
• Bằng cách nào chạy từng bước qua các lời gọi phương thức.
• Bằng cách nào kiểm tra và thay đổi giá trị của biến, dữ liệu thành viên của lớp..
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 9
Dò lỗi có thể được thực hiện theo nhiều cách. Thông thường qua thực đơn. Đơn giản nhất
là đặt điểm dùng bên thước trái. Ví dụ trong hình 2-5
Hình 2-5: Một điểm dừng
Để chạy Debug chúng ta có thể nhấn F5 và sau đó chương trình sẽ chạy đến điểm dừng như trong
hình 2.6
Hình 2-6: Chọn điểm dừng
Bằng cách đặt con chuột vào vị trí các biến chúng ta có thể thấy được giá trị hiện tại của biến như
trong hình 2-7.
Hình 2-7: Hiển thị một giá trị
Trình dò lỗi của Visual Studio .Net cũng cung cấp một số các cửa sổ hữu dụng khác để dó lỗi
như là cửa sổ Local để dò các biến cục bộ như trong hình 2.8
Hình 2-8: Cửa sổ Local
Chúng ta có thể mở rộng cửa sổ để xem chi tiết thông tin biến như trong hình 2-9
Hình 2-9: Mở rộng cửa sổ Local
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 10
Chúng ta có thể lặp qua các phương thức bằng các nhấn F11. Ví dụ lặp qua phương thức
DrawWindow() của lớp WindowClass như trong hình 2-10
Hình 2-10: Lặp qua một phương thức
3. Cơ sở của ngôn ngữ C#
3.1 Kiểu
C# bắt buộc chúng ta phải khai báo kiểu cho kiểu đối tượng chúng ta tạo như (nguyên,
thực, chuỗi, cửa sổ, nút lệnh). Trong c# có 2 kiểu:
• Kiểu xây dựng sẵn
• Kiểu người dùng tạo ra
C# cũng chia kiều thành 2 loại:
• Kiểu giá trị
• Kiểu tham chiếu
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 11
Khác nhau giữa hai kiểu trên chính là các thức giá trị của chúng được lưu trữ trong bộ nhớ.
Kiểu giá trị lưu trữ giá trị thật sự của nó trong bộ nhớ được cấp phát trên stack. Trong khi địa chỉ
của kiểu tham chiếu lưu trữ trên stack thì đối tượng thật sự lại lưu trữ trong heap.
C# cũng hỗ trợ kiểu con trỏ như trong c++.
3.1.1 Làm việc với kiểu xây dựng sẵn
Bảng 3-1: Các kiểu giá trị xây dựng sẵn trong C#
Kiểu Kích Kiểu .Net Mô tả
byte 1 Byte Không dấu 0-255
char 1 Char Ký tự unicode
bool 1 Boolean True hay false
sbyte 1 Sbyte Có dấu(-128-127)
short 2 Int16 Có dấu -32.768 – 32.767
ushort 2 Uint16 Không dấu (0-65353)
Int 4 Int32 Có dấu -2,147,483,647 2,147,483,647.
uint 4 Uint32 Không dấu 0 4,294,967,295.
float 4 Single +/-1.5 * 10--45 +/-3.4 * 1038
double 8 Double +/-5.0 * 10-324 +/-1.7 * 10308
Decimal 8 Decimal Lên đến 28 chũ số.
Long 8 Int64 -9,223,372,036,854,775,808 9,223,372,036,854,775,807
Ulong 8 Uint64 0 đến 0xffffffffffffffff.
3.1.1.1 Chọn kiểu xây dựng sẵn
Kiểu char được biểu diễn trong dạng Unicode. Unicode và ký tự escape phải được gắn với
dấu “”. Ví dụ, A là một ký tự đơn giản trong khi \u00041 là một ký tự unicode.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 12
Bảng 3-2: Một số ký tự phổ
biến
3.1.1.2 Chuyển kiểu xây dựng sẵn
Đối tượng của một kiểu có thể được chuyển thành một đối tượng khác theo cách tường
minh hay không tường minh. Chuyển kiểu tường minh sẽ thực hiện chuyển tự động và trình biên
dịch sẽ quản lý giùm chúng ta. Ví dụ:
short x = 5; int y = x;
Nếu chúng ta chuyển theo một các khác chúng ta có thể mất thông tin. Trình biên dịch sẽ
không thực hiện chuyển kiểu rõ ràng từ kiểu short sang int. Ví dụ:
short x; int y = 500; x = y;
Chúng ta phải khai báo rõ ràng:
short x; int y = 500; x = (short)y;
3.2 Biến và hằng
Một biến là một vị trí lưu trữ với một kiểu. Trong ví dụ trên, cả x và y là biến. Chúng ta
tạo biến bằng cách khai báo kiểu và gán tên cho nó. Chúng ta có thể khởi tạo biến khi chúng ta
khao báo nó. Chúng ta có thể gán giá trị đến biến bất cứ lúc nào. Ví dụ minh họa:
class Values { static void Main( ) { int myInt = 7; System.Console.WriteLine("Khởi tạo, myInt: {0}",myInt); myInt = 5; System.Console.WriteLine("Sau khi gán, myInt: {0}", myInt); }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 13
}
3.2.1 Định nghĩa gán
C# yêu cầu biến được khởi tạo trước khi chúng được sử dụng. Ví dụ sau sử dụng một biến
không được khởi gán:
class Values { static void Main( ) { int myInt; System.Console.WriteLine("Không khởi gán, myInt: {0}",myInt); myInt = 5; System.Console.WriteLine("Khởi gán, myInt: {0}", myInt); } }
Khi chạy chương trình sẽ thông báo lỗi sau:
Thật sự chúng ta không cần khởi tạo một biến nhưng chúng ta cần gán một giá trị trước khi sử
dụng chúng. Ví dụ sau minh họa một chương trình đúng
class Values { static void Main( ) { int myInt; myInt = 7; System.Console.WriteLine("Khởi gán, myInt: {0}", myInt); myInt = 5; System.Console.WriteLine("Giá trị sau khi gán, myInt: {0}", myInt); } }
3.2.2 Hằng
Môt hằng là một biến mà giá trị không bị thay đổi.
Cú pháp: Const kiểu tên_biến giá trị.
Ví dụ: const int Tile = 10;
Ví dụ minh họa sử dụng biến hằng
class Values { static void Main( ) { const int NhietDoDongLanh = 32; // độ Farenheit const int NhietDoSoi = 212; System.Console.WriteLine(" Nhiệt độ đông lạnh của nước: {0}", NhietDoDongLanh );
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 14
System.Console.WriteLine("Nhiệt độ sôi của nước: {0}", NhietDoSoi); } }
3.2.3 Kiểu liệt kê
Kiểu này bổ sung những tính năng mới thuận tiện hơn kiểu hằng. Kiểu liệt kê là một kiểu
giá trị phân biệt bao gồm một tập các tên hằng. Ví dụ chúng ta tạo 2 hằng liên quan nhau
const int NhietDoDongLanh = 32; // độ Farenheit const int NhietDoSoi = 212; Chúng ta có thể bổ sung một số hằng khác vào trong danh sách như:
const int NhietDoCoTheBoi= 72; // độ Farenheit Quá trình này thực hiện rất cồng kềnh. Do đó, chúng ta có thể dùng danh sách liệt kê để
giải quyết vấn đề:
enum NhietDo { NhietDoDongLanh = 32; NhietDoSoi = 212; NhietDoCoTheBoi= 72; }
Mỗi kiểu liệt kê phải có một kiểu bên dưới, chúng có thể là(int, short, long..) ngoại trừ
kiểu char. Cú pháp
Ví dụ sau minh họa dùng kiểu enum để định nghĩa một thực đơn cho chương trinh
enum Menu { Thoat=5, VeTamGiac, VeTamGiacLatNguoc, VeTamGiacGiuaManHinh, VeTamGiacRong }; static void ThucDon() { while(true) { int menu=0; Console.WriteLine("Nhap {0} de ve tam giac",(int)Menu.VeTamGiac); Console.WriteLine("Nhap {0} de ve tam giac giua man hinh",(int)Menu.VeTamGiacGiuaManHinh); Console.WriteLine("Nhap {0} de ve tam giac lat nguoc ",(int)Menu.VeTamGiacLatNguoc); Console.WriteLine("Nhap {0} de ve tam giac rong ",(int)Menu.VeTamGiacRong); Console.WriteLine("Nhap {0} de thoat ",(int)Menu.Thoat); menu = int.Parse(Console.ReadLine());
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 15
switch (menu) { case (int)Menu.VeTamGiac: VeTamGiac(); break; case (int)Menu.VeTamGiacGiuaManHinh: VeTamGiacGiuaManHinh(); break; case (int)Menu.VeTamGiacLatNguoc: VeTamGiacLatNguoc(); break; case (int)Menu.VeTamGiacRong: VeTamGiacRong(); break; default: return; } } }
3.2.4 Kiểu chuỗi
Đối tượng string lưu trữ một chuỗi các ký tự. Khi chúng ta khai báo một biến chuỗi sử
dụng từ khóa string chúng ta phải tạo một thể hiện của chuỗi:
string myString; sau đó gán giá trị cho biến myString.
hay dùng khai báo sau:
string myString = “Chuong trinh dau tien”;
3.3 Lệnh rẽ nhánh
3.3.1 lệnh if … else
Cú pháp :
if ( biểu thức)
lệnh 1
else
lệnh 2
Ví dụ minh họa lệnh rẽ nhánh
using System; class Values { static void Main( ) { int valueOne = 10; int valueTwo = 20; if ( valueOne > valueTwo ) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 16
Console.WriteLine( "Giá trị một: {0} lớn hơn giá trị hai: {1}", valueOne, valueTwo); } else { Console.WriteLine( "Giá trị hai: {0} lớn hơn giá trị một: {1}", valueTwo,valueOne); } valueOne = 30; //gán lại giá trị mới if ( valueOne > valueTwo ) { valueTwo = valueOne++; Console.WriteLine("\nGán giá trị một cho giá trị hai, "); Console.WriteLine("và tăng giá trị một lên hai.\n"); Console.WriteLine("Giá trị một: {0} , Giá trị hai: {1}", valueOne, valueTwo); } else { valueOne = valueTwo; Console.WriteLine("Gán hai giá trị bằng nhau và bằng giá trị hai. "); Console.WriteLine("Giá trị một: {0} giá trị hai: {1}", valueOne, valueTwo); } } }
3.3.2 Lệnh switch (một thay đổi của lệnh if lồng nhau)
Cú pháp: switch (expression) {
case constant-expression: statement jump-statement [default: statement]
} Ví dụ:
class Values { static void Main( ) { int chonThucDon; chonThucDon = 2; switch (chonThucDon) { case 1: System.Console.WriteLine(" Chọn món 1"); break; case 2:
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 17
System.Console.WriteLine(" Chọn món 1"); break; default: System.Console.WriteLine(" Phải chọn món có trong thực đơn 1"); break; } } }
3.4 Lệnh lặp
3.4.1 Lệnh goto
Để sử dụng lệnh goto chúng ta cần:
• Tạo nhãn
• Goto trên nhãn
Ví dụ minh họa sử dụng lệnh goto
using System; public class Tester { public static int Main( ) { int i = 0; repeat: // gán nhãn cho lệnh goto Console.WriteLine("i: {0}",i); i++; if (i < 10) goto repeat; return 0; } }
3.4.2 Lệnh lặp while
Cú pháp: while (biểu thức) lệnh;
Ví dụ dùng lệnh while
using System; public class Tester { public static int Main( ) { int i = 0; while (i < 10) { Console.WriteLine("i: {0}",i); i++; } return 0; } }
3.4.3 Lệnh do…while
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 18
Cú pháp: do biểu_thức while lệnh.
using System; public class Tester { public static int Main( ) { int i = 11; do { Console.WriteLine("i: {0}",i); i++; } while (i < 10); return 0; } }
3.4.4 Lệnh for
Cú pháp: for (khởi tạo; biểu_thức; lặp) lệnh;
Ví dụ:
using System; public class Tester { public static int Main( ) { for (int i=0;i<100;i++) { Console.Ghi("{0} ", i); if (i%10 == 0) { Console.WriteLine("\t{0}", i); } } return 0; } }
3.4.5 Lệnh continue và break
Thỉnh thoảng chúng ta muốm quay lại vòng lặp mà không cần thực hiện các lệnh còn lại
trong vòng lặp, chúng ta có thể dùng lệnh continue.
Ngược lại, nếu chúng ta muốn thoát ra khỏi vòng lặp ngay lập tức chúng ta có thể dùng
lệnh break;
Ví dụ:
using System; public class Tester { public static int Main( ) { string signal = "0";
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 19
while (signal != "X") { Console.Ghi("Nhập vào tín hiệu: "); signal = Console.ReadLine( ); Console.WriteLine("Tính hiệu vừa nhập: {0}", signal); if (signal == "A") { Console.WriteLine("Lỗi, bỏ qua\n"); break; } if (signal == "0") { Console.WriteLine("Bình thường.\n"); continue; } Console.WriteLine("{0}Tạo tín hiệu tiếp tục !\n",signal); } return 0; } }
3.5 Toán tử
Một toán tử là một ký hiệu mà nguyên nhân làm C# thực hiện một hành động. Các kiểu cơ bản
trong C# hỗ trợ các toán tử như gán, cộng, trừ, tăng, giảm. Các toán tử này tương tự như c++;
3.6 Không gian tên
Để khai báo không gian tên chúng ta sử dụng từ khóa namespace. Ví dụ:
namespace Programming_C_Sharp { using System; public class Tester { public static int Main( ) { for (int i=0;i<10;i++) { Console.WriteLine("i: {0}",i); } return 0; } } } Ví dụ sau khai báo không gian tên lồng nhau:
namespace LapTrinh_C_Sharp { namespace LapTrinh _C_Sharp_Test { using System; public class Tester { public static int Main( )
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 20
{ for (int i=0;i<10;i++) { Console.WriteLine("i: {0}",i); } return 0; } } } }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 21
Phần 2: LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG 1. Lớp và đối tượng
Khả năng tạo các kiểu dữ liệu mới là đặc trưng của ngôn ngữ lập trình hướng đối tượng.
Chúng ta có thể tạo ra kiểu mới bằng cách khai báo và định nghĩa lớp. Thể hiện của một lớp gọi
là một đối tượng. Đối tượng được tạo trong bộ nhớ khi chương thực thi.
Một thuận lợi lớn nhất của các lớp trong ngôn ngữ lập trình hướng đối tượng là chúng có
khả năng đóng gói các đặc trưng và khả năng của một thực thể trong một đơn vị mã chương trình.
1.1 Định nghĩa lớp (class)
Để định nghĩa một kiểu mới hay một lớp chúng ta đầu tiên khai báo nó và sau đó định
nghĩa các phương thức và trường của nó. Chúng ta khai báo lớp sử dụng từ khóa class.
Cú pháp:
[attributes] [access-modifiers] class identifier [:base-class ] { class-body }
Thông thường một lớp sẽ dùng từ khóa public là một access-modifiers. Indentifier là tên của lớp.
Nội dung của lớp được định nghĩa trong {}.
Trong C# mọi thứ xảy ra trong một lớp. Ví dụ sau định nghĩa một lớp Tester:
public class Tester {
public static int Main( ) { /... }
} Lúc khai báo một lớp mới, chúng ta định nghĩa thuộc tính của tất cả đối tượng cũng như
những hành vi của lớp đó. Ví dụ: một Listbox trong Window có các đặc trưng sau: độ cao, rộng,
vị trí, màu… và những người lập trình mong muốn các hành vi trong Listbox như chúng có thể
mở, đóng hay sắp xếp.
Ngôn ngữ lập trình hướng đối tượng phép chúng ta tạo một kiểu mới ListBox, đồng thời
đóng gói những đặc trưng và khả năng của chúng. Lúc đó, lớp Listbox có thể có các biến thành
viên như độ rộng, cao và các hàm thành viên Sort(), Add()…
Chúng ta không thể gán dữ liệu đến kiểu Listbox, đầu tiên chúng ta phải tạo một đối tượng kiểu
Listbox theo đoạn mã sau.
ListBox myListBox.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 22
Một khi chúng ta tạo một thể hiện của ListBox chúng ta có thể gán dữ liệu đến các trường
của nó.
Xem xét một lớp theo dõi và hiển thị thời gian của ngày. Trạng thái bên trong của lớp phải
có khả năng biểu diễn năm, tháng, ngày, giờ, phút và giây hiện tại. Chúng ta muốn thời gian hiển
thị ở nhiều dạng khác nhau. Chúng ta có thể thực hiện được yêu cầu của lớp trên bằng cách khai
báo một lớp định nghĩa một phương thức và sáu biến như sau:
using System; public class Time { // public methods public void DisplayCurrentTime( ) { Console.WriteLine("Hiển thị thời gian hiện tại"); } // private variables int Year; int Month; int Date; int Hour; int Minute; int Second; } public class Tester { static void Main( ) { Time t = new Time( ); t.DisplayCurrentTime( ); } }
Phương thức DisplayCurrentTime() được định nghĩa trả về kiểu void. Nó không trả về giá
trị cho phương thức gọi nó. Trong hàm Main() thể hiện của lớp Time được tạo và địa chỉ của nó
được gán đến đối tượng t. Bởi vì t là thể hiện của đối tượng Time nên nó có thể sử dụng phương
thức DisplayCurrentTime()để gọi phương thức hiển thị thời gian:
t.DisplayCurrentTime( ); 1.1.2 Kiểu truy xuất
Kiểu truy xuất xác định phương thức của lớp(bao gồm phương thức của những lớp khác)
có thể nhìn và sử dụng biến thành viên hay phương thức bên trong lớp. Bảng sau liệt kê các kiểu
truy xuất
Kiểu truy
xuất
Hạn chế
Public Không hạn chế. Thành viên được đánh dấu public được nhìn thấy
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 23
bởi bất kỳ phương thức của bất kỳ lớp
private Các thành viên trong lớp A được đánh dấu private được truy xuất
chỉ trong các phương thức của lớp A
protected Các thành viên trong lớp A được đánh dấu protected được truy
xuất trong các phương thức của lớp A và các lớp dẫn cuất từ A
internal Các thành viên trong lớp A được đánh dấu internal được truy xuất
trong các phương thức của bất kỳ lớp trong assembly của A
protected
internal
Tương đương với protected or internal
Biến thành viên của lớp nên được khai báo như sau:
private int Year; private int Month; private int Date; private int Hour; private int Minute; private int Second;
1.1.3 Tham số của phương thức
Phương thức có thể có số tham số bất kỳ. Danh sách tham số theo sau bởi tên phương thức,
mỗi tham số phải được gán một kiểu dữ liệu. Ví dụ sau định nghĩa một tên phương thức
MyMethod và trả về giá trị void, nó nhận hai tham số int và button
void MyMethod (int firstParam, button secondParam) { // ... }
Bên trong phần thân của phương thức, tham số hoạt động như là một biến cục bộ. Ví dụ
sau minh họa bằng cách nào đưa giá trị trong một phương thức, trong trường hợp này giá trị có
kiểu int và float
using System; public class MyClass { public void SomeMethod(int firstParam, float secondParam) { Console.WriteLine("Các tham số nhận được: {0}, {1}",firstParam, secondParam); } } public class Tester { static void Main( ) { int howManyPeople = 5; float pi = 3.14f;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 24
MyClass mc = new MyClass( ); mc.SomeMethod(howManyPeople, pi); } }
1.2 Tạo đối tượng
Trong chương trước chúng ta phân biệt giữa kiểu giá trị và kiểu tham chiếu. Các kiểu cơ
bản trong C# là kiểu giá trị và chúng được tạo trên stack. Đối tượng, tuy nhiên nó là một kiểu
tham chiếu, được tạo trên heap sử dụng từ khóa new. Ví dụ:
Time t = new Time();
t không thật sự chứa đối tượng Time, nó chỉ chứa địa chỉ của Time được tạo trên Heap. t chỉ thật
sự là một tham chiếu của đối tượng đó.
1.2.1 Phương thức tạo lập
Trong lệnh sau:
Time t = new Time(); Một phương thức được gọi bất cứ lúc nào chúng ta tạo một thể hiện cho một đối tượng gọi
là phương thức tạo lập. Nếu chúng ta không định nghĩa nó trong phần khai báo lớp, CLR sẽ cung
cấp một phương thức mặc định đại diện cho nó. Nhiệm vụ của phương thức tạo lập là tạo đối
tượng chỉ bởi lớp và đặt đối tượng vào trong trạng thái sẵn sàng. Trước khi phương thức tạo lập
chạy, đối tượng chưa tồn tại trong bộ nhớ, sau khi tạo lập hoàn thành, bộ nhớ lưu trữ một thể hiện
hợp lệ của một kiểu lớp.
Trong ví dụ lớp Time không định nghĩa một phương thức tạo lập, trình biên dịch sẽ tự động tạo
một phương thức tạo lập cho nó. Khi sử dụng phương thức tạo lập mặc định các biến được khởi
tạo giá trị mặc định như sau:
Kiếu Giá trị mặc định
Kiểu số (int, long..) 0
Bool False
Char ‘\0’
Enum 0
Reference null
Thông thường chúng ta định nghĩa riêng một phương thức tạo lập và cung cấp tham số cho
phương thức tạo lập để khởi tạo các biến cho đối tượng của chúng ta.
Để định nghĩa một phương thức tạo lập, chúng ta khai báo một phương thức có tên trùng
với tên lớp mà chúng ta khai báo. Phương thức này không trả về bất cứ giá trị nào và thông
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 25
thường được khai báo public. Tham số sử dụng trong phương thức tạo lập cũng như những tham
số trong phương thức khác.
Ví dụ sau khai báo một phương thức tạo lập cho lớp Time và chấp nhận một tham số có kiểu là
DateTime
using System; namespace QuanLySinhVien { public class SinhVien { private float _diem; public string maso; public string ten; public bool gioitinh; public float diem; public SinhVien(string maso, string ten, bool gioitinh, float diem) { this.maso = maso; this.ten = ten; this.gioitinh = gioitinh; this.diem = diem; } private string GT(bool gt) { return gt?"Male":"Female"; } public override string ToString() { return maso + "\t" + ten + "\t" + GT(this.gioitinh) + "\t" + diem.ToString(); } } static void Main(string[] args) { //Khởi tạo một biến s thuộc kiểu sinh viên SinhVien s = new SinhVien(“001”,”Thanh”,false,8); //Xuất thộng tin sinh viên ra màn hình Console.WriteLine(s); } } 1.2.2 Khởi tạo
Thay vì khởi tạo giá trị các biến thành viên thông qua phương thức tạo lập chúng ta có thể
thực hiện gán giá trị khởi tạo trực tiếp cho biến. Phương thức tạo lập của chương trình trên có thể
được viết như sau:
public void SinhVien() { Console.Ghi("Nhap maso "); maso = Console.ReadLine(); Console.Ghi("Nhap ten "); ten = Console.ReadLine(); Console.Ghi("Nhap gioi tinh 1 cho Nam, 0 cho Nu ");
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 26
int i = int.Parse(Console.ReadLine()); if(i==1) this.gioitinh = true; else this.gioitinh = false; Console.Ghi("Nhap diem "); diem = float.Parse(Console.ReadLine()); }
1.2.3 Phương thức tạo lập sao chép
Phương thức tạo lập sao chép tạo một đối tượng mới bằng cách chép các biến từ đối tượng
hiện tại đến đối tượng mới cùng kiểu. Ví dụ chúng ta muốn truyền một đối tượng SinhVien đến
một đối tượng SinhVien của phương thức tạo lập nhằm tạo ra đối tượng mới có cùng giá trị với
đối tượng củ. C# không cung cấp phương thức tạo lập sao chép, do đó chúng ta phải tự tạo. Ví
dụ:
public SinhVien(SinhVien sv) { this.maso = sv.maso; this.ten = sv.ten; this.gioitinh = sv.gioitinh; this.diem = sv.diem; } Dựa trên việc khai báo phương thức tạo lập bên trên ta có thể tạo một đối tượng sinh viên b từ một đối tượng sinh viên a như sau: SinhVien a = new SinhVien(“001”,”Thanh”,false,8); SinhVien b = new SinhVien(a)
1.2.4 Từ khóa this
Từ khóa this chỉ ra trạng thái hiện tại của đối tượng. Tham chiếu this(con trỏ this) là một con trỏ
ẩn đến các hàm không tĩnh(không khai báo từ khóa static) của lớp. Mỗi phương thức có thể tham
chiếu đến các phương thức và biến khác dựa trên tham chiếu this.
Tham chiếu this có thể sử dụng theo ba trường hợp sau:
• Tránh xung đột tên
• Truyền đối tượng hiện tại là tham số của một phương thức khác
• Dùng với chỉ mục
1.3. Sử dụng các thành viên tĩnh
Thuộc tính và phương thức của một lớp có thể là thành viên thể hiện hay thành viên tĩnh.
Thành viên thể hiện được kết hợp với thể hiện của kiểu, trong khi thành viên tĩnh được coi là
phần của lớp. Chúng ta truy xuất các thành viên tĩnh thông qua tên lớp chúng ta khai báo.
using System; using System.Web;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 27
namespace Test { class Mang1Chieu { static int []a = new int[100]; static int n = 0; static void Main(string[] args) { Nhap(); //Chúng ta cũng có thể gọi phương thức tĩnh như sau Mang1Chieu.Xuat(); Console.WriteLine("Tong cac phan tu mang la " + TinhTong().ToString()); Console.ReadLine(); } void NhapNgauNhien() { Console.Ghi("Nhap vao chieu dai cua mang n = "); n = int.Parse(Console.ReadLine()); Random r = new Random(); for(int i=0; i < n; i++ ) a[i] = r.Next(10); } static void Nhap() { Console.Ghi("Nhap vao chieu dai cua mang n = "); n = int.Parse(Console.ReadLine()); for(int i=0; i < n; i++ ) { Console.Ghi("a[{0}] = ",i); a[i] = int.Parse(Console.ReadLine()); } } static int TinhTong() { int sum = 0; int i = 0; while(i<n) sum += a[i++]; return sum; } static void Xuat() { Console.Ghi("Mang vua nhap la "); for(int i=0; i < n; i++ ) Console.Ghi("{0} ", a[i]); } } } Trong ví dụ trên chúng ta khai báo lớp Mang1Chieu và có các phương thức tĩnh như Nhap, Xuat,
chúng ta có thể gọi thực hiện các phương thức này nhưng không cần thông qua đối tượng hay thể
hiện của lớp.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 28
Trong C#, sẽ không hợp lệ nếu chúng ta truy xuất thành viên tĩnh qua thể hiện.
1.3.1 Gọi phương thức tĩnh
Hàm Main() là một phương thức tĩnh. Phương thức tĩnh hoạt động trên lớp hơn là trên thể
hiện của lớp. Chúng không có tham chiếu this để trỏ đến phương thức tĩnh.
Phương thức tĩnh không thể truy xuất trực tiếp từ các thành viên không tĩnh.
Trong ví dụ trên phương thức NhapNgauNhien là phương thức không tĩnh, do đó phải được truy
cập thông qua đối tượng như sau:
Ví dụ:
static void Main(string[] args) { Mang1Chieu a = new Mang1Chieu(); a.NhapNgauNhien(); Xuat(); Console.WriteLine("Tong cac phan tu mang la " + TinhTong().ToString()); Console.ReadLine(); }
1.3.2 Sử dụng phương thức tạo lập tĩnh
Nếu chúng ta khai báo một phương thức tạo lập tĩnh, chúng ta phải đảm bảo phương thức
tạo lập tĩnh sẽ chạy trước khi thể hiện của lớp được tạo.
Ví dụ, chúng ta có thể thêm phương thức tạo lập tĩnh vào lớp Mang1Chieu
static Mang1Chieu( ) { } Chúng ta không được khai báo kiểu truy xuất trước phương thức tạo lập tĩnh. Thêm vào đó, vì đây là một phương thức thành viên tĩnh, chúng ta không thể truy xuất vào biến thành viên không tĩnh, do đó tất cả các biến muốn được truy cập thông qua các thành viên tĩnh phải được khai báo static. 1.3.3 Sử dụng trường tĩnh
Dùng biến thành viên tĩnh là cách phổ biến để theo dõi số thể hiện hiện tại của lớp. Ví dụ:
using System; public class Cat { public Cat( ) { thehien ++; } public static void SoMeo( ) { Console.WriteLine("Số thể hiện của mèo la{0} ",thehien); } private static int thehien = 0; }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 29
public class Tester { static void Main( ) { Cat.SoMeo( ); Cat Tom= new Cat( ); Cat.SoMeo( ); Cat Linda = new Cat( ); Cat.SoMeo( ); } }
1.4. Hủy đối tượng
C# cung cấp một bộ “dọn rác” và do đó chúng ta không cần một phương thức hủy rõ ràng
để xóa các đối tượng trong bộ nhớ. Phương thức Finalize() được gọi bởi bộ dọn rác khi đối
tượng của chúng ta bị hủy. Hàm này chỉ giải phóng các tài nguyên mà đối tượng này đang giữ và
nó không tham chiếu đến các đối tượng khác.
Khai báo:
~MyClass() { ///Mã } Hay
MyClass.Finalize() { ///Mã }
1.5 Truyền tham số
Mặc định các kiểu giá trị được truyền vào trong các phương thức bởi giá trị. Tuy nhiên
trong nhiều trường hợp chúng ta muốn truyền giá trị đối tượng theo tham chiếu. C# cung cấp từ
khóa ref để truyền giá trị đối tượng vào trong phương thức và từ khóa out để truyền giá trị đối
tượng vào trong phương thức mà không cần khởi gán. Ngoài ra, C# còn cung cấp từ khóa params
để cho phép một phương thức truyền số tham số tùy ý.
1.5.1 Truyền tham chiếu
Một phương thức chỉ có thể trả về một giá trị, do đó khi muốn phương thức trả về nhiều
giá trị, chúng ta dùng cách thức truyền tham chiếu
Ví dụ, thực hiện đoạn chương trình sau;
using System; namespace Bien { class Class1
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 30
{ static int a = 1; static int b = 2; static void GiaTri(int a, int b) { a = 10; b = 20; } static void Main(string[] args) { int a = 1; Console.WriteLine("a = {0}, b = {1}",a,b); GiaTri(a, b); Console.WriteLine("a = {0}, b = {1}",a,b); Console.ReadLine(); } } } Kết quả thự hiện chương trình là a =1, b =2 và a=1, b=2 nhưng nếu chúng ta khai báo phương thức GiaTri như sau: static void GiaTri(ref int a, int b) { a = 10; b = 20; } Thì kết quả thực hiện chương trình sẽ là a =1, b =2 và a=10, b=2. Ta thấy giá trị của biến a thay
đổi nhưng giá trị của biến b không thay đổi sau khi chúng ta thực hiện gọi phương thức GiaTri vì
biến a được truyền theo kiểu tham biến còn biến b được truyền theo kiểu tham trị.
1.5.2 Truyền tham số dùng từ khóa out
Măc định C# quy định tất cả các biến phải được gán tham số trước khi sử dụng. Trong ví
dụ trên nếu chúng ta không khởi tạo biến a trong hàm Main chương trình thông dịch sẽ thông báo
lỗi biến chưa được khởi gán như sau Use of unassigned local variable 'a'. Chúng ta có thể tránh
bằng cách dùng từ khóa out và khai báo như sau:
using System; namespace Bien { class Class1 { static int a = 1; static int b = 2; static void GiaTri(out int a, int b) { a = 10; b = 20; } static void Main(string[] args) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 31
int a = 1; GiaTri(out a, b); Console.WriteLine("a = {0}, b = {1}",a,b); Console.ReadLine(); } } }
1.6 Quá tải phương thức và phương thức tạo lập
Thông thường chúng ta muốn khai báo nhiều hàm với cùng tên. Ví dụ như chúng ta muốn
nhiều phương thức tạo lập khác nhau. Dấu hiệu của một phương thức được định nghĩa bởi tên và
danh sách tham số. Hai phương thức có dấu hiệu khác nhau nếu có tên và danh sách tham số khác
nhau.
Tham số khác nhau bởi số tham số hay kiểu khác nhau. Ví dụ các hàm sau có các dấu hiệu khác
nhau:
void myMethod(int p1); void myMethod(int p1, int p2); void myMethod(int p1, string s1); Ví dụ minh họa sử dụng quá tải hàm cho phương thức tạo lập của lớp MaTran using System; namespace MaTran { public class MaTran { public double[,] mt; public int hang; public int cot; #region Phuong thuc tao lap public MaTran() { //Phuong thuc tao lap mac dinh } public MaTran(int h, int c) { this.hang = h; this.cot = c; mt = new double[h, c]; } public MaTran(MaTran a) { mt = new double[a.hang, a.cot]; this.hang = a.hang; this.cot = a.cot; for (int i = 0; i < a.hang; i++) for (int j = 0; j < a.cot; j++) this.mt[i, j] = a.mt[i, j]; } public MaTran(MaTran a, int h, int c)
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 32
{ mt = new double[h, c]; this.hang = h; this.cot = c; for (int i = 0; i < h; i++) for (int j = 0; j < c; j++) this.mt[i, j] = a.mt[i, j]; } public void Nhap() { Console.WriteLine("Nhap mot ma tran cap {0}x{1} ", this.hang, this.cot); for (int i = 0; i < this.hang; i++) for (int j = 0; j < this.cot; j++) { //Console.Ghi("Nhap phan tu thu [{0},{1}] = ",i,j); this.mt[i, j] = i + j; } } public void Nhap(int h, int c) { mt = new double[h, c]; for (int i = 0; i < h; i++) for (int j = 0; j < c; j++) { Console.Ghi("Nhap phan tu thu [{0},{1}] = ", i, j); this.mt[i, j] = double.Parse(Console.ReadLine()); } } public override string ToString() { string kq = ""; for (int i = 0; i < this.hang; i++) { kq += "\n"; for (int j = 0; j < this.cot; j++) { kq += "\t" + this.mt[i, j].ToString(); } } return kq; } public MaTran Tong(MaTran a, MaTran b) { MaTran kq = new MaTran(a.hang, a.cot); for (int i = 0; i < b.hang; i++) for (int j = 0; j < b.cot; j++) kq.mt[i, j] = a.mt[i, j] + b.mt[i, j]; return kq; } public void Tong(MaTran b) { for (int i = 0; i < b.hang; i++) for (int j = 0; j < b.cot; j++) this.mt[i, j] = this.mt[i, j] + b.mt[i, j]; }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 33
} }
Trong ví trên chúng ta khai báo ba phương thức tạo lập khác nhau cho việc khởi tạo một đối
tượng kiểu ma trận. Tương tự chúng ta cũng định nghĩa nhiều phương thức Tong với các tham số
khác nhau cho việc cộng hai ma trận.
1.7 Đóng gói dữ liệu với thuộc tính
Các thuộc tính cho phép các client truy xuất trạng thái lớp như là truy xuất các trường
thành viên trực tiếp của lớp. Truy xuất thuộc tính tương tự như truy xuất phương thức của lớp.
Nghĩa là các client có thể truy xuất trực tiếp đến trạng thái của đối tượng(thuộc tính) mà không
cần thông qua việc gọi thực thi các phương thức. Tuy nhiên, những người thiết kế lớp hay muốn
che dấu trạng thái bên trong của các thành viên lớp và chỉ muốn các thuộc tính chỉ trược truy xuất
gián tiếp thông qua phương thức của lớp.
Bằng cách phân tách thuộc tính với các phương thức của lớp, các nhà thiết kế có thể tự do
thay đổi giá trị hay trạng thái của các thuộc tính bên trong của đối tượng khi cần. Cho ví dụ như
sau
using System; namespace DoiTuongHinhHoc { public class HinhTron { private double r; public HinhTron() { } public HinhTron(double bankinh) { this.R = bankinh; } public double DienTich() { return Math.Round(Math.PI*R*R,2); } public override string ToString() { return "Duong tron co ban kinh " + R + " dien tich " + this.DienTich(); } } }
Lúc lớp HinhTron được tạo, giá trị của thuộc tính bán kính r có thể được lưu trữ là một
biến thành viên. Lúc lớp được thiết kế lại, giá trị của thuộc tính r có thể được tính toán lại hay lấy
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 34
từ cơ sở dữ liệu. Nếu client truy xuất trực tiếp biến thành viên r, những thay đổi giá trị tính toán
có thể làm hỏng ứng dụng client. Bằng cách phân tách và bắt buộc client muốn truy cập thuộc
tính r phải sử dụng thông qua phương thức. Ta có thể khai báo như sau để cho phép đối tượng có
thể truy cập biến r thông qua phương thức:
using System; namespace DoiTuongHinhHoc { public class HinhTron { private double r; public double R { get { return r; } set { r = value; } } public HinhTron() { } public HinhTron(double bankinh) { this.R = bankinh; } public double DienTich() { return Math.Round(Math.PI*R*R,2); } public override string ToString() { return "Duong tron co ban kinh " + R + " dien tich " + this.DienTich(); } } }
Thuộc tính đáp ứng cả hai mục đích:
• Chúng cung cấp một giao tiếp đơn giản với Client, xuất hiện là biến thành viên
• Chúng thực hiện như là một phương thức. Tuy nhiên, chúng cung cấp cơ chế che dấu dữ
liệu bởi một thiết kế hướng đối tượng
Để khai báo thuộc tính, chúng ta viết kiểu thuộc tính và tên theo sau bởi {}. Bên trong {}
chúng ta có thể khai báo cách thức truy xuất get hay set. Qua phương thức set chúng ta có một
tham số ẩn value.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 35
Trong ví dụ trên R là một thuộc tính, nó khai báo 2 kiểu truy xuất
public int R {
get { return r; } set { r = value; }
} Giá trị của thuộc tính R có thể được lưu trữ trong cơ sở dữ liệu, trong biến thành viên private
thậm chí lưu trữ tại một máy tính hay một dịch vụ nào đó trong mạng.
1.7.1 Truy xuất get
Phần thân của truy xuất get thì tương tự như phần thân của phương thức lớp. Chúng trả về
một đối tượng có kiểu la kiểu của thuộc tính.Trong ví dụ trên, truy xuất get của R trả về giá trị
double. Nó trả về giá trị của biến thành viên private r.
Bất cứ lúc nào chúng ta tham chiếu đến thuộc tính, truy xuất get được yêu cầu để đọc giá trị của
thuộc tính
HinhTron t = new HinhTron (1,5); double bankinh = t.R;
1.7.2 Truy xuất set
Truy xuất set dùng để gán giá trị cho thuộc tính, nó tương tự như một phương thức lớp trả
về kiểu void. Lúc chúng ta định nghĩa một truy xuất set, chúng ta phải sử dụng từ khóa
value(tham số ẩn) để biểu diễn tham số nơi mà giá trị được truyền và lưu trữ trong thuộc tính:
set { r = value; }
Lúc chúng ta gán một giá trị đến một thuộc tính, truy xuất set tự động được yêu cầu và
tham số ẩn value được đặt giá trị mà chúng ta cần gán cho thuộc tính:
Ví dụ ta có thể dùng lệnh sau trong một phương thức nào đó của lớp HinhTron để tăng giá trị của
thuộc tính r lên 1: R++;
Thuận lợi của các tiếp cận này là client có thể truy xuất trực tiếp thuộc tính mà không cần
quan tâm tới các dữ liệu ẩn bên trong.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 36
1.8 Các trường chỉ đọc (readonly)
Nếu chúng ta muốn tạo một phiên bản của lớp Time sau có nhiệm vụ cung cấp một giá trị tĩnh,
biểu diễn ngày và thời gian hiện tại. Chúng ta có thể dùng khai báo readonly để khai báo các
thuộc tính:
public class RightNow { static RightNow( ) { System.DateTime dt = System.DateTime.Now; Year = dt.Year; Month = dt.Month; Date = dt.Day; Hour = dt.Hour; Minute = dt.Minute; Second = dt.Second; } public static int Year; public static int Month; public static int Date; public static int Hour; public static int Minute; public static int Second; } public class Tester { static void Main( ) { System.Console.WriteLine ("This year: {0}", RightNow.Year.ToString( )); RightNow.Year = 2002; System.Console.WriteLine ("This year: {0}", RightNow.Year.ToString( )); } }
Trong trường hợp này, giá trị của biến year sẽ vẫn thay đổi khi chúng ta thực hiện phép
gán year = 2002. Chúng ta muốn đánh dấu các giá trị tĩnh là hằng nhưng không thể vì chúng ta
không khởi gán giá trị cho chúng cho tới khi phương thức tạo lập tĩnh được thực hiện. C# cung
cấp từ khóa readonly dùng cho mục đích này. Nếu chúng ta thay đổi khai báo biến thành viên của
lớp như sau:
public static readonly int Year; public static readonly int Month; public static readonly int Date; public static readonly int Hour; public static readonly int Minute; public static readonly int Second; và không dùng lệnh // RightNow.Year = 2002; // Thực hiện lệnh này sẽ gây ra lỗi Trình biên dịch thực hiện và chạy theo đúng yêu cầu của chúng ta.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 37
2. Kế thừa(inheritance) và đa hình(polymorphism)
Trong phần trước minh họa cách tạo một kiểu dữ liệu mới bằng các khai báo lớp. Trong
phần này tìm hiểu mối quan hệ giữa các đối tượng trong thế giới thực và mô hình hóa những
quan hệ này trong mã chương trình như thế nào? Phần này quan tâm khả năng chuyên biệt hóa
được thực hiện trong C# thông qua tính kế thừa của ngôn ngữ lập trình hướng đối tượng.
2.1 Chuyên biệt hóa và tổng quát hóa
Các lớp và thể hiện của chúng không tồn tại trong một không gian độc lập, chúng tồn tại
trong một mạng các quan hệ và phụ thuộc qua lại lẫn nhau.
Quan hệ is-a là một sự chuyên biệt hóa. Lúc chúng ta nói “Chó” là một “động vật”, nghĩa
là “Chó” là một loại “động vật” chuyên biệt. Nó có đặc trưng của bất kỳ động vật nào, nhưng nó
có những đặc trưng của thú nuôi trong nhà. “Mèo” cũng là một động vật và chúng ta mong muốn
chúng chia sẻ những đặc trưng tổng quát với “Chó” nhưng “Mèo” lại có những đặc trưng riêng
biệt khác với “Chó”.
Quan hệ tổng quát hóa và chuyên biệt hóa là quan hệ phân cấp và tương hỗ lẫn nhau.
Tương hỗ vì chuyên biệt hóa là mặt đối lập với tổng quát hóa. Và những quan hệ này là phân cấp
vì chúng tạo ra cây quan hệ.
Chúng ta nói ListBox và Button là chuyên biệt hóa của một lớp Window. Nghĩa là chúng
có những đặc trưng và hành vi của lớp Window mà chúng ta mong muốn tìm thấy trong cả hai
kiểu này. Theo một nghĩa khác lớp Window tổng quát các đặc trưng chia sẻ của cả ListBox và
Button. Trong khi nó có sự chuyên biệt các đặc trưng và hành vi của từng đối tượng.
Hình vẽ sau minh họa một quan hệ is-a
Thông thường hai lớp chia sẻ chung những chức năng, những chức năng này được đặt trên
lớp cơ sở. Nó cung cấp một cơ chế tái sử dụng lại các đoạn mã chung và khả năng quản lý code.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 38
2.2 Kế thừa
Trong C#, quan hệ chuyên biệt hóa thông thường được thực hiện thông qua sự kế thừa.
Đây không phải là cách duy nhất để thực hiện sự kế thừa. Tuy nhiên nó là các phổ biến và tự
nhiên để thực hiện quan hệ này.
Chúng ta nói ListBox kế thừa từ Window có nghĩa nó là một Window chuyên biệt. Window
được gọi là một lớp base(cơ sở) và ListBox gọi là lớp derived (dẫn xuất). Thật vậy, ListBox kế
thừa từ những đặc trưng và hành vi của lớp Window và rồi chuyên biệt hóa những chu cầu riêng
của nó.
2.2.1 Thực thi sự kế thừa
Trong C# chúng ta có thể tạo một lớp dẫn xuất bằng cách thêm dấu hai chấm theo sau tên
của lớp dẫn xuất cùng với tên của lớp cơ sở
public class ListBox : Window Trong khai báo trên, khai báo ListBox là lớp dẫn xuất từ Windows. Lớp dẫn xuất kế thừa
tất cả thành viên của lớp cơ sở, cả biến và hàm thành viên. Lớp dẫn xuất có thể tự do thực hiện
những phiên bản riêng của mình đối với các phương thức lớp cơ sở. Nó thực hiện điều này bằng
cách đánh dấu phương thức mới bằng từ khóa new. Dùng từ khóa này để chỉ ra lớp dẫn xuất
chúng ta muốn che dấu và thay thế phương thức ở lớp cơ sở.
Ví dụ sau minh họa một lớp dẫn xuất
using System; public class Window { public Window(int top, int left) { this.top = top; this.left = left; } public void DrawWindow( ) { Console.WriteLine("Vẽ cửa sổ tại vị trí {0}, {1}", top, left); } private int top; private int left; } public class ListBox : Window { public ListBox( int top, int left, string theContents): base(top, left) // gọi phương thức tạo lập của lớp cơ sở { mListBoxContents = theContents;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 39
} // dùng từ khóa new vì chúng ta muốn thay đổi phương thức vẽ cửa sổ tại lớp dẫn xuất public new void DrawWindow( ) { base.DrawWindow( ); // gọi hàm vẽ cửa sổ của lớp cha Console.WriteLine ("In một chuỗi: {0}", mListBoxContents); } private string mListBoxContents; // khai báo biến thành viên mới } public class Tester { public static void Main( ) { // Tạo thể hiện của lớp Windows Window w = new Window(5,10); w.DrawWindow( ); // Tạo thể hiện của lớp dẫn xuất ListBox ListBox lb = new ListBox(20,30,"Hello world"); lb.DrawWindow( ); } }
2.2.2 Gọi phương thức tạo lập của lớp cơ sở
Trong ví dụ trên lớp ListBox dẫn xuất từ lớp Window và có phương thức tạo lập riêng của
nó với ba tham số. Phương thức tạo lập của ListBox gọi phương thức tạo lập của lớp cha bằng
cách đặt dấu “:” sau danh sách tham số và gọi lớp cơ sở với từ khoá base:
public ListBox(int theTop, int theLeft, string theContents):base(theTop, theLeft) // một cách thức khác để gọi phương thức tạo lập của lớp cơ sở
Bởi vì các lớp không thể kế thừa phương thức tạo lập của lớp cơ sở nên một lớp dẫn xuất
phải thực thi phương thức tạo lập riêng của mình và chúng có thể sử dụng phương thức tạo lập
của lớp cơ sở theo cách rõ ràng như trên
Chú ý trong ví dụ trên lớp dẫn xuất Listbox thực thi một phiên bản mới của phương thức
DrawWindow();
public new void DrawWindow( )
Từ khoá new chỉ ra rằng người lập trình muốn tạo ra một phiên bản mới của phương thức
này trong lớp dẫn xuất.
Nếu lớp cơ sở có một phương thức tạo lập có thể truy xuất mặc định. Khi phương thức tạo
lập của lớp dẫn xuất không yêu cầu phương thức tạo lập của lớp cơ sở khi đó phương thức tạo lập
mặc định được gọi theo kiểu không tường minh. Tuy nhiên nếu lớp cơ sở không có một phương
thức tạo lập mặc định, các lớp dẫn xuất phải gọi rõ ràng một phương thức tạo lập cơ sở sử dụng
từ khóa base.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 40
2.2.3 Gọi phương thức lớp cơ sở
Trong ví dụ trên phương thức DrawWindow() của lớp ListBox ẩn và thay thế phương thức
DrawWindow() của lớp cơ sở. Lúc chúng ta gọi DrawWindow() trong một đối tượng kiểu ListBox
thì phương thức LisBox.DrawWindow() được gọi chứ không phải phương thức DrawWindow()
trong lớp Window. Tuy nhiên, trong lớp ListBox ta vẫn có thể gọi phương thức DrawWindow()
của lớp Window.
2.2.4 Kiểm soát truy xuất
Hiển thị của lớp và các thành viên của lớp có thể được hạn chế bằng cách khai báo kiểu
truy xuất như public, private, protected, internal, protected internal.
Các lớp và các thành viên của nó có thể được thiết kế theo nhiều mức truy xuất khác nhau. Nếu
một thành viên của lớp có nhiều mức truy xuất khác nhau, mức hạn chế nhất được chọn. Thật vậy,
nếu chúng ta định nghĩa một lớp myClass như sau:
public class myClass { // … protected int myValue; } Thì truy xuất đối với biến myValue từ các đối tượng là protected mặc dù mức truy xuất của lớp
myClass là public.
2.3 Đa hình (polymorphism)
Hai khía cạnh mạnh nhất của kế thừa đó là khả năng sử dụng lại code và đa
hình(polymorphism). Poly có nghĩa là nhiều và morph nghĩa là hình thức. Thật vậy đa hình chính
là khả năng sử dụng nhiều hình thức của một kiểu mà không cần quan đến chi tiết của nó.
2.3.1 Tạo kiểu đa hình
Bởi vì ListBox is-a Window và Button is-a Window là những quan hệ kế thừa. Chúng ta
mong muốn có thể sử dụng một trong hai kiểu này trong tình huống chúng ta gọi Window. Ví dụ,
một form muốn giữ một tập tất cả các thể hiện Window mà nó quản lý lúc form được mở. Nó có
thể yêu cầu mỗi đối tượng Window vẽ riêng theo các của nó. Cho hoạt động này Form không cần
biết các đối tượng trong danh sách là ListBox hay Button. Nó chỉ muốn lấy ra một phần tử trong
tập hợp và nói chúng vẽ. Đó chính là Form muốn xử lý tất cả các đối tượng Window theo đặc
trưng đa hình.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 41
2.3.2 Tạo phương thức đa hình
Để tạo phương thức hỗ trợ đa hình, chúng ta cần đánh dấu dùng từ khóa virtual trong lớp
cơ sở của nó.Ví dụ để chỉ ra phương thức DrawWindow() trong lớp Window là đa hình chúng ta
dùng từ khóa theo khai báo sau:
public virtual void DrawWindow( ) Bây giờ các lớp dẫn xuất tự do thực hiện các phiên bản riêng của lớp DrawWindow(). Để
thực hiện điều này chúng ta đơn giản viết đè lên phương thức ảo của lớp cơ sở dùng từ khoá
override trong lớp dẫn xuất. Ví dụ:
public override void DrawWindow( ) { base.DrawWindow( ); // gọi hàm vẽ cửa sổ của lớp cha Console.WriteLine ("In một chuỗi: {0}", mListBoxContents); } Tương tự chúng ta có thể thực hiện điều này cho lớp Button. Hàm Main() của chương trình có thể được viết lại như sau Window win = new Window(1,2); ListBox lb = new ListBox(3,4,"Nội dung "); Button b = new Button(5,6); win.DrawWindow( ); lb.DrawWindow( ); b.DrawWindow( );
Chương trình có thể thực hiện như ta mong muốn. Đối tượng DrawWindow được gọi cho
mỗi đối tượng. Tuy nhiên không có tính đa hình được sử dụng ở đây. Muốn sử dụng đa hình
chúng ta phải khai báo sau:
Window[] winArray = new Window[3]; winArray[0] = new Window(1,2); winArray[1] = new ListBox(3,4,"List box in Array”); winArray[2] = new Button(5,6); Điều gì xảy ra khi ta gọi phương thức DrawWindow()? Ví dụ hoàn chỉnh như sau: public class Window { //Phương thức tạo lập dùng để khởi tạo giá trị có 2 biến chứa tọa độ của cửa sổ public Window(int top, int left) { this.top = top; this.left = left; } // hàm mô phỏng vẽ cửa sổ public virtual void DrawWindow( ) { Console.WriteLine("Cửa sổ vẽ tại tọa độ {0}, {1}", top, left); }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 42
//Hai biến thành viên được khai báo protected, do đó chúng có thể được nhìn thấy hay kế thừa từ lớp dẫn xuất. protected int top; protected int left; } //lớp ListBox dẫn xuất từ lớp Windows, lớp này bổ sung thêm thuộc tính listBoxContents chứa nội dung hiển thị trên ListBox. public class ListBox : Window { // phương thức tạo lập của lớp ListBox public ListBox(int top, int left,string contents):base(top, left) // gọi phương thức tạo lập của lớp cơ sở { listBoxContents = contents; } // Dùng từ khóa override bởi vì chúng ta muốn thay đổi hành vi của phương thức DrawWindow public override void DrawWindow( ) { base.DrawWindow( ); // gọi phương thức của lớp cha Console.WriteLine ("In nội dung listbox: {0}", listBoxContents); } private string listBoxContents; // khai báo biến thành viên mới } //Khai báo lớp Button kế thừa từ lớp Window public class Button : Window { public Button(int top,int left):base(top, left) { } // Dùng từ khóa override bởi vì chúng ta muốn thay đổi hành vi của phương thức DrawWindow public override void DrawWindow( ) { Console.WriteLine("vẽ nút tại ví trí {0}, {1}\n", top, left); } } public class Tester { static void Main( ) { Window win = new Window(1,2); ListBox lb = new ListBox(3,4,"Hien thi"); Button b = new Button(5,6); win.DrawWindow( ); lb.DrawWindow( ); b.DrawWindow( ); Window[] winArray = new Window[3]; winArray[0] = new Window(1,2); winArray[1] = new ListBox(3,4,"List box trong mảng"); winArray[2] = new Button(5,6); for (int i = 0;i < 3; i++) { winArray[i].DrawWindow( ); } }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 43
}
2.3.3 Phân biệt giữa từ khóa new và override
Trong C#, quyết định của lập trình viên nhằm ghi đè một phương thức ảo của lớp cơ bản
được thực hiện rõ ràng qua từ khóa override. Điều này cho phép chúng ta tạo ra một phiên bản
mới trong code chúng ta. Những thay đổi trong mã của lớp cơ sở không phá vỡ những đoạn mã
trong lớp dẫn xuất.
Trong C# một hàm ảo luôn được xem là gốc của các hàm ảo gởi đi. Thật vậy, khi C# tìm
một phương thức ảo, nó không tìm kiếm trong phân cấp kế thừa. Nếu một hàm ảo được đưa vào
trong lớp Window, hành vi thực thi của lớp ListBox vẫn không thay đổi.
Để tránh việc khai báo phương thức ảo bị chồng chéo, chúng ta có thể sử dụng từ khóa
new để chỉ ra không có phương thức ghi đè lên phương thức ảo của lớp cơ sở.
2.4 Lớp trừu tượng(abstract)
Mỗi lớp con của Window nên thực thi phương pháp DrawWindow() của riêng nó, nhưng
các lớp dẫn xuất không bắt buộc thực hiện điều này. Để yêu cầu tất cả các lớp dẫn xuất thực thi
một phương pháp của lớp cơ sở chúng ta cẩn chỉ ra phương thức đó là trừu tượng (abstract).
Phương thức được khai báo trừu tượng không cần định nghĩa hay khai báo nội dung thực
thi của nó . Nó chỉ nhằm tạo một tên phương thức và đánh dấu rằng nó phải được thi hành trong
tất cả các lớp dẫn xuất từ nó lớp dẫn xuất.
Chúng ta dùng từ khóa abstract để định nghĩa phương thức trừu tượng:
abstract public void DrawWindow( ); Ví dụ sử dụng phương pháp và lớp trừu tượng
public class Window { //Phương thức tạo lập dùng để khởi tạo giá trị có 2 biến chứa tọa độ của cửa sổ public Window(int top, int left) { this.top = top; this.left = left; } // hàm mô phỏng vẽ cửa sổ abtract virtual void DrawWindow( ) { Console.WriteLine("Cửa sổ vẽ tại tọa độ {0}, {1}", top, left); } //Hai biến thành viên được khai báo protected, do đó chúng có thể được nhìn thấy hay kế thừa từ lớp dẫn xuất. protected int top; protected int left; }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 44
//lớp ListBox dẫn xuất từ lớp Windows, lớp này bổ sung thêm thuộc tính listBoxContents chứa nội dung hiển thị trên ListBox. public class ListBox : Window { // phương thức tạo lập của lớp ListBox public ListBox(int top, int left,string contents):base(top, left) // gọi phương thức tạo lập của lớp cơ sở { listBoxContents = contents; } // Dùng từ khóa override bởi vì chúng ta muốn thay đổi hành vi của phương thức DrawWindow public override void DrawWindow( ) { base.DrawWindow( ); // gọi phương thức của lớp cha Console.WriteLine ("In nội dung listbox: {0}", listBoxContents); } private string listBoxContents; // khai báo biến thành viên mới } //Khai báo lớp Button kế thừa từ lớp Window public class Button : Window { public Button(int top,int left):base(top, left) { } // Dùng từ khóa override bởi vì chúng ta muốn thay đổi hành vi của phương thức DrawWindow public override void DrawWindow( ) { Console.WriteLine("vẽ nút tại ví trí {0}, {1}\n", top, left); } } public class Tester { static void Main( ) { Window win = new Window(1,2); ListBox lb = new ListBox(3,4,"Hien thi"); Button b = new Button(5,6); win.DrawWindow( ); lb.DrawWindow( ); b.DrawWindow( ); Window[] winArray = new Window[3]; winArray[0] = new Window(1,2); winArray[1] = new ListBox(3,4,"List box trong mảng"); winArray[2] = new Button(5,6); for (int i = 0;i < 3; i++) { winArray[i].DrawWindow( ); } } }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 45
2.4.1 Giới hạn của trừu trượng
Mặc dù thiết kế trừu tượng DrawWindow() bắt buột tất cả các lớp dẫn xuất thi hành
phương thức trừu tượng của nó. Điều này sẽ gây khó khăn nếu lớp dẫn xuất từ lớp ListBox không
bắt buộc thi hành phương thức DrawWindow();
2.4.2 Lớp Sealed
Ngược lại của abstract là sealed. Dùng từ khóa sealed, một lớp có thể không cho phép các
lớp khác dẫn xuất từ nó.
2.5 Gốc của tất cả các lớp là Object
Tất cả các lớp trong C# đều được dẫn xuất từ lớp Object. Lớp gốc là lớp trên nhất trong
cây phân cấp kế thừa. Trong C#, lớp gốc là Object. Object cung cấp một số các phương thức mà
các lớp con có thể ghi đè. Chúng gốm các phương thức sau:
Phương thức Ý nghĩa
Equal() Kiểm tra hai đối tượng có tương đương nhua không
GetHashCode() Cho phép đối tượng cung cấp hàm Hash riêng để sử dụng trong
kiểu tập hợp.
GetType() Cung cấp truy xuất đến kiểu đối tượng.
ToString() Cung cấp chuỗi biểu diễn đối tượng.
Finalize() Xóa đối tượng trong bộ nhớ.
MemberwiseClone() Tạo copy của đối tượng.
Ví dụ sau minh họa sử dụng phương thức ToString() kế thừa từ đối tượng cũng như các kiểu dữ
liệu cơ bản mà chúng được kế thừa từ Object
using System; public class Test { public Test (int val) { value = val; } public override string ToString( ) { return value.ToString( ); } private int value; } public class Tester { static void Main( ) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 46
int i = 5; Console.WriteLine("Giá trị của biến i la: " + i)); Test s = new Test (7); Console.WriteLine("Giá trị của biến s la " +s ); } } Trong trường trên hàm ToString() của đối tượng s sẽ được gọi tự động.
2.6 Kiểu đóng hộp(Boxing) và mở hộp (unboxing)
Đóng hộp và mở hộp là quá trình xử lý cho phép các kiểu giá trị được xử lý như là kiểu
tham chiếu. Đóng hộp lá đóng giá trị bên trong một đối tượng và mở hộp trả về kiểu giá trị của
đối tượng đó. Đó là cách cho phép chúng ta gọi phương thức ToString() trên số nguyên trong ví
dụ trên.
2.6.1 Đóng hộp là dạng không tường minh
Đóng hộp chuyển một kiểu giá trị đến một kiểu Object không tường minh. Đóng hộp một
giá trị sẽ cấp phát một thể hiện của Object và chép giá trị vào trong thể hiện của đối tượng mới
như trong hình vẽ sau:
Ví dụ minh họa:
using System; class Boxing { public static void Main( ) {
int i = 123; Console.WriteLine("The object value = {0}", i);
} } 2.6.2 Mở hộp tường minh
Để lấy giá trị từ đối tượng hộp trả về một kiểu giá trị, chúng ta phải mở hộp rõ ràng. Chúng ta
nên thực hiện hai bước sau:
• Đảm bảo thể hiện đối tượng là một giá trị đóng hộp để đưa kiểu giá trị.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 47
• Chép giá trị từ thể hiện đến biến kiểu giá trị
Minh họa như trong hình vẽ sau:
Để thực hiện được quá trình mở hộp, đối tượng bị mở hộp phải là một tham chiếu đến đối
tượng mà chúng được tạo bởi đóng hộp một giá trị của kiểu được đưa. Minh họa mở và đóng hộp
như sau:
using System; public class UnboxingTest { public static void Main( ) { int i = 123; //đóng hộp object o = i; // mở hộp phải được khai báo tường minh int j = (int) o; Console.WriteLine("j: {0}", j); } }
2.7 Các lớp lồng nhau
Lớp có các thành viên(thuộc tính và phương thức). Thành viên của lớp có thể là một kiểu
dữ liệu do người dùng định nghĩa. Các lớp lồng nhau có thuận lợi là truy xuất được tất cả các
thành viên của các lớp bên ngoài. Phương thức của lớp lồng nhau có thể truy xuất những
thành viên private của lớp ngoài. Thêm vào đó các lớp lồng nhau có thể che dấu tất cả từ các
lớp khác. Cuối cùng các lớp lồng nhau được truy xuất public bên trong phạm vi của lớp ngoài.
Ví dụ sử dụng lớp lồng nhau:
using System; using System.Text; public class PhanSo { public PhanSo(int tu, int mau) { this.tu=tu; this.mau=mau; }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 48
public override string ToString( ) { StringBuilder s = new StringBuilder( ); s.AppendFormat("{0}/{1}",tu, mau); return s.ToString( ); } internal class HienThiPhanSo { public void Ve(PhanSo f) { Console.WriteLine("Phan tu: {0}",f.tu);
Console.WriteLine("Phan mau: {0}",f.mau); } } private int tu; private int mau; } public class Tester { static void Main( ) { PhanSo f1 = new PhanSo(3,4); Console.WriteLine("f1: {0}", f1.ToString( )); PhanSo.PhanSoArtist fa = new PhanSo.PhanSoArtist( ); fa.Ve(f1); } }
3. Sử dụng từ khóa operator
Trong C#, operator được định nghĩa là một phương thức tĩnh và phải có trả về kết quả.
Khi chúng ta tạo một toán tử cho lớp chúng ta có thể quá tải toán tử đó tương tự như cách chúng
ta quá tải hàm. Ví dụ quá tải toán tử cộng chúng ta có thể viể như sau:
public static PhanSo operator+(PhanSo lhs, PhanSo rhs) Tham số đầu tiên lhs là toán hạng bên trái và tham số thứ hai là toán hạng bên phải.
Dùng từ khóa operator theo sau bởi toán tử để quá tải. Chúng ta có thể tạo các toán tử như các
toán tử logic, toán tử tuơng đương (==).
Ví dụ sau minh họa dùng quá tải toán tử cho ứng dụng xây dựng kiểu dữ liệu phân số:
using System; namespace ConsoleApplication6.PhanSo1 { public class PhanSo { public int tu; private int _mau; public int mau {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 49
get { return _mau; } set { if (value == 0) { Console.WriteLine("Mau so phai khac 0"); _mau = 1; } else _mau = value; } } //Các phương thức tạo lập cho phân số public PhanSo() { } public PhanSo(int tu, int mau) { this.tu = tu; this.mau = mau; } public PhanSo(PhanSo a) { this.tu = a.tu; this.mau = a.mau; } //Các phương thức nhập phân số public void Nhap() { //Nhập giá trị cho tử Console.Ghi(" Nhap tu = "); this.tu = int.Parse(Console.ReadLine()); //Nhập giá trị cho mẫu Console.Ghi(" Nhap mau = "); this.mau = int.Parse(Console.ReadLine()); } //Chuyển phân số thành một chuỗi public override string ToString() { return tu + "/" + mau; } //Cộng hai phân số public static PhanSo operator +(PhanSo a, PhanSo b) { PhanSo kq = new PhanSo(); kq.tu = a.tu * b.mau + a.mau * b.tu; kq.mau = a.mau * b.mau; return kq.RutGon(); } //Cộng phân số với số x.... a/b = (a+x)/(b+x) public static PhanSo operator +(PhanSo a, int x) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 50
PhanSo kq = new PhanSo(); kq.tu = a.tu + x; kq.mau = a.mau + x; return kq.RutGon(); } public static PhanSo operator -(PhanSo a, PhanSo b) { PhanSo kq = new PhanSo(); kq.tu = a.tu * b.mau - a.mau * b.tu; kq.mau = a.mau * b.mau; return kq.RutGon(); } public static PhanSo operator -(PhanSo a, int x) { PhanSo kq = new PhanSo(); kq.tu = a.tu - x; kq.mau = a.mau - x; return kq.RutGon(); } public static PhanSo operator *(PhanSo a, PhanSo b) { PhanSo kq = new PhanSo(); kq.tu = a.tu * b.tu; kq.mau = a.mau * b.mau; return kq.RutGon(); } //nhân phân số a với một số nguyên x a/b * x = a/b*x public static PhanSo operator *(PhanSo a, int x) { PPhanSo tam = new PhanSo((PhanSo)x); return a * tam(); } public static PhanSo operator /(PhanSo a, PhanSo b) { return a * b.NghichDao(); } //chia phân số a với một số nguyên x a/b chia x = a/b*x public static PhanSo operator /(PhanSo a, int x) { PhanSo tam = new PhanSo((PhanSo)x); return a * tam.NghichDao(); } public static PhanSo operator ++(PhanSo a) { PhanSo kq = new PhanSo(); kq.tu = a.tu + 1; kq.mau = a.mau + 1; return kq.RutGon(); } public static PhanSo operator --(PhanSo a) { PhanSo kq = new PhanSo();
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 51
kq.tu = a.tu--; kq.mau = a.mau--; return kq; } //Chuyển số nguyên thành phân số public static implicit operator PhanSo(int a) { PhanSo kq = new PhanSo(); kq.tu = a; kq.mau = 1; return kq; } //Chuyển phân số thành số thực public static explicit operator double(PhanSo a) { return ((double)a.tu / (double)a.mau); } public PhanSo RutGon() { int us = this.USCLN(); PhanSo tam = new PhanSo(); tam.tu = this.tu / us; tam.mau = this.mau / us; return tam; } private int USCLN() { int a, b; //Kiểm tra trường hợp nếu tử và mẫu âm a = tu > 0 ? tu : -tu; b = mau > 0 ? mau : -mau; while (a != b) { if (a > b) a = a - b; if (b > a) b = b - a; } return a; } public PhanSo NghichDao() { PhanSo kq = new PhanSo(); kq.tu = this.mau; kq.mau = this.tu; return kq; } } } using System; namespace ConsoleApplication6.PhanSo1 { public class KiemTra
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 52
{ public KiemTra() { } public static void KiemTraPhuongThucCong() { PhanSo ps1 = new PhanSo(-16,5); Console.WriteLine("Phan so thu nhat 1"+ ps1); PhanSo ps2 = new PhanSo(20,2); Console.WriteLine("Phan so thu nhat 2 "+ ps2); Console.WriteLine("Cong phan so mot va hai "+ (ps1 + ps2)); } } }
4. Kiểu cấu trúc (Struct)
Cấu trúc là một kiểu người dùng định nghĩa đơn giản. Cấu trúc thì tương tự như lớp, chúng có thể
chứa thuộc tính, trường, phương thức hay toán tử. Tuy nhiên cấu trúc không có sự kế thừa và
phương thức hủy. Quan trọng nhất cấu trúc là một kiểu giá trị trong khi lớp là kiểu tham chiếu.
Do đó cấu trúc rất hữu ích cho việc biểu diễn các đối tượng mà chúng không yêu cầu ngữ nghĩa
tham chiếu.
4.1 Định nghĩa cấu trúc
[attributes] [access-modifiers] struct identifier [:interface-list] { struct-members } Ví dụ sau minh họa định nghĩa một cấu trúc Vitri biểu diễn một điểm trong không gian 2 chiều.
using System; public struct Vitri { public Vitri(int xCoordinate, int yCoordinate) { xVal = xCoordinate; yVal = yCoordinate; } public int x { get { return xVal; } set { xVal = value; } } public int y { get {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 53
return yVal; } set { yVal = value; } } public override string ToString( ) { return (String.Format("{0}, {1}", xVal,yVal)); } private int xVal; private int yVal; } public class Tester { public void myFunc(Vitri loc) { loc.x = 50; loc.y = 100; Console.WriteLine("Loc1 Vitri: {0}", loc); } static void Main( ) { Vitri loc1 = new Vitri(200,300); Console.WriteLine("Loc1 Vitri: {0}", loc1); Tester t = new Tester( ); t.myFunc(loc1); Console.WriteLine("Loc1 Vitri: {0}", loc1); } }
4.2 Tạo cấu trúc
Chúng ta tạo một thể hiện của một cấu trúc bằng cách sử dụng từ khóa new trong lệnh gán khi
chúng ta muốn nó như là một lớp. Trong ví dụ trên lớpTester tạo một thể hiện của Vitri như sau:
Vitri loc1 = new Vitri(200,300); Bạn thử thực hiện lệnh sau trong chương trình để xem và giải thích kết quả:
Vitri loc1 = new Vitri(200,300); Console.WriteLine("Loc1 Vitri: {0}", loc1);
5. Giao tiếp (interface)
Một giao tiếp được xem là một liên lạc nhằm thông tin đến client rằng một lớp hay một
cấu trúc sẽ được thực hiện như thế nào? Khi một lớp được thi hành interface, nó sẽ nói với client
rằng nó sẽ hỗ trợ phương thức, thuộc tính, sự kiện và chỉ mục).
Interface là một dạng thay đổi của lớp trừu tượng nhằm tạo ra sự liên lạc giữa các lớp và
client của chúng. Chúng ta sử dụng từ khóa interface để khai báo một tham chiếu và đóng gói sự
liên lạc.
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 54
Về mặt cú pháp, một giao tiếp giống như một lớp chỉ có phương thức trừu trượng. Giao
tiếp là cách để trộn lẫn với các cây kế thừa khác.
Khi một lớp thực thi một giao tiếp, nó phải thực hiện tất cả phương thức của giao tiếp đó
5.1 Thực thi một giao tiếp
Cú pháp [attributes] [access-modifier] interface interface-name [:base-list] {
Nội dung }
Giả sử chúng ta muốn tạo một giao tiếp mô tả phương thức và thuộc tính của lớp cần để
lưu trữ và lấy dữ liệu từ cơ sở dữ liệu hay từ một tập tin. Chúng ta có thể gọi giao tiếp này là
ILuuTru.
Giao tiếp này có thể chi ra hai phương thức: Doc() và Ghi().
Chúng ta định nghĩa như sau:
interface ILuuTru {
void Doc( ); void Ghi(object);
} Ví dụ chúng ta có thể tạo lớp TaiLieu. Kiểu TaiLieu có thể được lưu trữ trong cơ sở dữ liệu và
chúng ta quyết định lớp TaiLieu thực thi giao tiếp ILuuTru. Khi đó lớp TaiLieu có thể được khai
báo như sau:
public class TaiLieu : ILuuTru { public void Doc( ) {...} public void Ghi(object obj) {...} } Ví dụ được minh họa trong chương trình sau: using System; // khai báo Interface interface ILuuTru { // không được định nghĩa kiểu truy xuất, phương thức không được định nghĩa thực thi void Doc( ); void Ghi(object obj); int TinhTrang { get; set; } } // Tạo một lớp thực thi giao tiếp ILuuTru public class TaiLieu : ILuuTru { public TaiLieu(string s) { Console.WriteLine("Tạo tài liệu: {0}", s); } // Thực thi phương thứ Doc
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 55
public void Doc( ) { Console.WriteLine( "Thực hiện phương thức Doc của ILuuTru"); } // Thực thi phương thứ Ghi public void Ghi(object o) { Console.WriteLine( " Thực hiện phương thức Ghi của ILuuTru "); } // implement the property public int TinhTrang { get { return tinhtrang; } set { tinhtrang = value; } } // Gán giá trị cho biến tinh trang private int tinhtrang = 0; } // Take our interface out for a spin public class Tester { static void Main( ) { // Truy xuất các phương thức trong lớp TaiLieu TaiLieu doc = new TaiLieu("Kiểm tra tài liệu"); doc.TinhTrang = -1; doc.Doc( ); Console.WriteLine("Tình trạng tài liệu: {0}", doc.TinhTrang); // chuyển và sử dụng interface ILuuTru isDoc = (ILuuTru) doc; isDoc.TinhTrang = 0; isDoc.Doc( ); Console.WriteLine("Tình trang ILuuTru: {0}", isDoc.TinhTrang); } }
5.1.1 Thực thi nhiều giao tiếp
Các lớp có thể thực thi nhiều hơn một giao tiếp. Ví dụ lớp TaiLieu cần có hai phương thức
lưu trữ hay nén tài liệu. Do đó chúng ta phải chọn thực thi cả hai giao tiếp ILuuTru và INen.
public class TaiLieu : ILuuTru, INen Sau khi thực hiện khai báo trên trong lớp tài liệu phải có hai phương thức trong lớp TaiLieu như sau: public void Nen( ) {
Console.WriteLine("Thực hiện nén");
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 56
} public void GiaiNen( ) {
Console.WriteLine("Thưc hiện giải nén"); }
5.1.2 Mở rộng giao tiếp
Chúng ta có thể mở rộng giao tiếp hiện tại bằng cách bổ sung những thành viên hay
phương thức mới cũng như thay đối phương thức đang làm việc. Ví dụ chúng ta có thể mở rộng
giao tiếp INen thành InhatKyNen nhằm lưu trữ các thông tin trong quá trình nén tài liệu, chúng ta
mở rộng lớp INen và bổ sung phương thức LuuNhatKy nhằm theo dõi số byte được ghi .
interface INhatKyNen : INen {
void LuuNhatKy( ); }
Tương tự chúng ta có thể tạo ra lớp giao tiếp bằng cách kết hợp các lớp giao tiếp hiện tại.
Ví dụ chúng ta muốn tạo lớp ILuuTruNen. Giao tiếp này có thể kết hợp các phương thức của cả
hai giao tiếp trong ví dụ trên và cũng bổ sung một phương thức mới để lưu trữ kích thước của tập
tin nén.
interface ILuuTruNen : ILuuTru, INhatKyNen {
void LuuTruKichThuoc(); } Ví dụ minh họa và mở rộng giao tiếp using System; interface ILuuTru {
void Doc( ); void Ghi(object obj); int TinhTrang
} // Định nghĩa interface INen interface INen {
void Nen( ); void GiaiNen( );
} // Mở rộng Interface interface INhatKyNen : INen {
void LuuNhatKy( ); } // Kết hợp Interface interface ILuuTruNe : ILuuTru, INhatKyNen {
void LuuTruKichThuoc( );
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 57
} // Định nghĩa giao tiếp khác interface MaHoa {
void Mahoa() void GiaiMa( );
} public class TaiLieu : ILuuTruNen, MaHoa { // Phương thức tạo lập của lớp TaiLieu public TaiLieu(string s) {
Console.WriteLine("Đang tạo tài liệu: {0}", s); } // Thực thi các hàm trong ILuuTru public void Doc( ) {
Console.WriteLine("Thực hiện phương thức Doc của ILuuTru"); } public void Ghi(object o) {
Console.WriteLine("Thực hiện phương thức Doc của ILuuTru"); } public int TinhTrang { get {
return tinhtrang; } set {
tinhtrang = value; } } // Thực hiện các phương thức của giao tiếp INen public void Nen( ) {
Console.WriteLine("Thực hiện phương thức Nen"); } public void GiaiNen( ) {
Console.WriteLine("Thực hiện phương thức GiaiNen"); } // Thực hiện các phương thức của giao tiếp INhatKyNen public void LuuNhatKy( ) { Console.WriteLine("Thực hiện phương thức LuuNhatKy"); } public void LuuTruKichThuoc( ) { Console.WriteLine("Thực hiện phương thức LuuTruKichThuoc"); } // Thực hiện các phương thức của giao tiếp MaHoa public void Mahoa( ) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 58
Console.WriteLine("Thực hiện phương thức Encrypt"); } public void GiaiMa( ) {
Console.WriteLine("Thực hiện phương thức GiaiMa"); } // lưu trữ dữ liệu cho thuộc tính TinhTrang của giao tiếp ILuuTru private int tinhtrang = 0; } public class Tester { static void Main( ) { // Tạo một đối tượng kiểu tài liệu TaiLieu doc = new TaiLieu("Test TaiLieu"); // chuyển đối tượng tài liệu đến kiểu giao tiếp ILuuTru ILuuTru isDoc = doc as ILuuTru; if (isDoc != null) {
isDoc.Doc( ); } else
Console.WriteLine("ILuuTru không hỗ trợ"); // chuyển đối tượng tài liệu đến kiểu giao tiếp INen INen icDoc = doc as INen; if (icDoc != null) {
icDoc.Nen( ); } else
Console.WriteLine("Không hỗ trợ nén"); INhatKyNen ilcDoc = doc as INhatKyNen; if (ilcDoc != null) {
ilcDoc.LuuNhatKy( ); ilcDoc.Nen( );
} else
Console.WriteLine("Không hỗ trợ ghi nhật ký theo dõi"); ILuuTruNen isc = doc as ILuuTruNen; if (isc != null) { isc.LuuTruKichThuoc( ); // ILuuTruNen isc.LuuNhatKy( ); // INhatKyNen isc.Nen( ); // INen isc.Doc( ); // ILuuTru } else { Console.WriteLine("Không hỗ trợ lưu trữ nén"); } MaHoa ie = doc as MaHoa; if (ie != null) {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 59
ie.Mahoa( ); } else
Console.WriteLine("Không hỗ trợ mã hóa"); } }
5.2 Truy xuất phương thức trong lớp giao tiếp
Chúng ta có thể truy xuất thành viên của giao tiếp ILuuTru nếu chúng là thành viên của lớp TaiLieu ILuuTru isDoc = (ILuuTru) doc; isDoc.status = 0; isDoc.Doc( ); hay chúng ta có thể tạo thể hiện của một giao tiếp bằng cách chuyển tài liệu đến kiểu giao tiếp và dùng giao tiếp để truy xuất phương thức: ILuuTru isDoc = (ILuuTru) doc; isDoc.status = 0; isDoc.Doc( ); 5.2.1 Sử dụng toán tử is
Chúng ta có thể hỏi một đối tượng có hỗ trợ giao tiếp hay không để gọi các phương thức thích hợp. Trong C# có hai cách để thực hiện điều này. Cách thứ nhất là dùng toán tử is Cú pháp: Expression is type Toán tử này trả về true nếu biểu thức có thể được chuyển đến kiểu an toàn mà không có biệt lệ. Ví dụ minh họa: using System; interface ILuuTru { void Doc( ); void Ghi(object obj); int TinhTrang { get; set; } } interface INen { void Nen( ); void GiaiNen( ); } public class TaiLieu : ILuuTru { public TaiLieu(string s) { Console.WriteLine("Đang tạo tài liệu: {0}", s); } // ILuuTru.Doc public void Doc( ) { Console.WriteLine("Thực hiện phương thức Doc của ILuuTru");
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 60
} // ILuuTru.Ghi public void Ghi(object o) { Console.WriteLine("Thực hiện phương thức Ghi của ILuuTru"); } // ILuuTru.TinhTrang public int TinhTrang { get { return tinhtrang; } set { tinhtrang = value; } } private int tinhtrang = 0; } public class Tester { static void Main( ) { TaiLieu doc = new TaiLieu("Kiểm tra TaiLieu"); // chỉ chuyển kiểu nếu nó là an toàn if (doc is ILuuTru) { ILuuTru isDoc = (ILuuTru) doc; isDoc.Doc( ); } // Khi thực hiện lệnh sau sẽ gây ra lỗi if (doc is INen) { INen icDoc = (INen) doc; icDoc.Nen( ); } } }
5.2.2 Sử dụng toán tử as
Toán tử as kết hợp is và hoạt động chuyển đổi kiểu. Trong ví dụ trên thay vì dùng toán tử
is ta có thể dùng toán tử as như sau:
static void Main( ) {
TaiLieu doc = new TaiLieu("Kiểm tra TaiLieu"); ILuuTru isDoc = doc as ILuuTru; if (isDoc != null) isDoc.Doc( ); else Console.WriteLine("Không hỗ trợ ILuuTru "); INen icDoc = doc as INen;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 61
if (icDoc != null) icDoc.Nen( ); else
Console.WriteLine("Không hỗ trợ nén"); }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 62
CASE STUDY Mục tiêu:
Thực hiện phân tích các yêu cầu của ứng dụng
Thiết kế các lớp và mối quan hệ của các lớp
Sử dụng các đặc trưng của hướng đối tượng như đóng gói, kế thừa và đa hình trong quá
trình xây dựng và phát triển ứng dụng.
Sử dụng các kiểu dữ liệu sẵn có được hỗ trợ bởi .Net trong quá trình phát triển ứng dụng.
Sử dụng kiểu truy xuất cho các thành viên của lớp.
I. Case study 1
Xây dựng ứng dụng thực hiện thực hiện tính tổng diện tích của các đối tượng hình học được bỏ
vào trong một cái hộp. Các đối tượng hình học gồm hình tròn, vuông, chữ nhật và hình tam giác.
I. 1 Chương trình không sử dụng kế thừa và được thiết kế như sau:
Mô hình hóa ứng dụng thành lược đồ lớp như sau:
Nội dung của lớp HinhTamGiac
using System; namespace DoiTuongHinhHoc.DTHH {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 63
public class HinhTamGiac { public double a; public double b; public double c; public double p { get { return ChuVi()/2; } } public HinhTamGiac() { } public HinhTamGiac(double c1, double c2, double c3) { this.a = c1; this.b = c2; this.c = c3; } public double ChuVi() { return a+b+c; } public double DienTich() { return Math.Round(Math.Sqrt(p*(p-a)*(p-b)*(p-c)),2); } public override string ToString() { return "Tam giác co 3 canh (" + a + "," + b + "," + c + ") dien tich " + this.DienTich(); } } } Nội dung của lớp HinhTron
using System; namespace DoiTuongHinhHoc { public class HinhTron { private double r; public double R { get { return r; } set { r = value;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 64
} } public HinhTron() { } public HinhTron(double bankinh) { this.R = bankinh; } public double DienTich() { return Math.Round(Math.PI*R*R,2); } public override string ToString() { return "Duong tron co ban kinh " + R + " dien tich " + this.DienTich(); } } } Nội dung của lớp HinhVuong
using System; namespace DoiTuongHinhHoc.DTHH { public class HinhVuong { public double a; public HinhVuong() { } public HinhVuong(double canh) { this.a = canh; } public HinhVuong(HinhVuong hv) { this.a = hv.a; } public double DienTich() { return Math.Round(a*a,2); } public override string ToString() { return "Hinh vuong co canh " + a + " dien tich " + this.DienTich(); } } } Nội dung của lớp TongDienTich
using System; namespace DoiTuongHinhHoc.DTHH { public class TongDienTich {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 65
HinhTron []dsTron = new HinhTron[100]; int soluongTron=0; HinhVuong []dsVuong = new HinhVuong[100]; int soluongVuong=0; HinhTamGiac []dsTamGiac = new HinhTamGiac[100]; int soluongTamGiac=0; public TongDienTich() { dsTron[0]= new HinhTron(3); dsTron[1]= new HinhTron(5); dsTron[2]= new HinhTron(6); soluongTron =3; this.dsTamGiac[0] = new HinhTamGiac(3,4,5); this.dsTamGiac[1] = new HinhTamGiac(3,3,3); soluongTamGiac=2; dsVuong[0] = new HinhVuong(3); dsVuong[1] = new HinhVuong(5); dsVuong[2] = new HinhVuong(6); dsVuong[3] = new HinhVuong(8); soluongVuong = 4; } public double TinhTongDienTich() { double tong=0; for(int i=0; i <soluongTron; i++) tong += dsTron[i].DienTich(); for(int i=0; i <soluongVuong; i++) tong += dsVuong[i].DienTich(); for(int i=0; i <soluongTamGiac; i++) tong += dsTamGiac[i].DienTich(); return tong; } public override string ToString() { string s=""; for(int i=0; i <soluongTron; i++) s += dsTron[i] + "\n"; for(int i=0; i <soluongVuong; i++) s += dsVuong[i]+ "\n"; for(int i=0; i <soluongTamGiac; i++) s += dsTamGiac[i]+ "\n"; return s; } } }
Nội dung của lớp Class1
using System; using DoiTuongHinhHoc.DTHH; namespace DoiTuongHinhHoc {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 66
class Class1 { static void Main(string[] args) { TongDienTich dt = new TongDienTich(); Console.WriteLine("Danh sach cac doi tuong hinh hoc"); Console.WriteLine(dt); Console.WriteLine("Tong dien tich cua cac hinh " + dt.TinhTongDienTich()); Console.ReadLine(); } } }
I. 2 Chương trình không sử dụng kế thừa và được thiết kế như sau:
Chương trình sau được thiết kế dựa trên tính kế thừa, đa hình và sử dụng interface. Ngoài ra còn
thực hiện sắp xếp các đối tượng trong hộp theo chiều tăng của diện tích và sử dụng ArrayList để
lưu trữ các đối tượng hình học.
Mô hình hóa ứng dụng thành lược đồ lớp như sau:
Nội dung của inteface IHinhHoc
using System; namespace DoiTuongHinhHoc.DTHH {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 67
public interface IHinhHoc { double DienTich(); } } Nội dung của lớp HinhTamGiac
using System; namespace DoiTuongHinhHoc.DTHH { public class HinhTamGiac:IHinhHoc { public double a; public double b; public double c; public double p { get { return ChuVi()/2; } } public HinhTamGiac() { } public HinhTamGiac(double c1, double c2, double c3) { this.a = c1; this.b = c2; this.c = c3; } public double ChuVi() { return a+b+c; } public double DienTich() { return Math.Round(Math.Sqrt(p*(p-a)*(p-b)*(p-c)),2); } public override string ToString() { return "Tam giác co 3 canh (" + a + "," + b + "," + c + ") dien tich " + this.DienTich(); } } } Nội dung của lớp HinhVuong
using System; namespace DoiTuongHinhHoc.DTHH { public class HinhVuong:IHinhHoc {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 68
public double a; public HinhVuong() { } public HinhVuong(double canh) { this.a = canh; } public virtual double DienTich() { return Math.Round(a*a,2); } public override string ToString() { return "Hinh vuong co canh " + a + " dien tich " + this.DienTich(); } } } Nội dung của lớp HinhChuNhat
using System; namespace DoiTuongHinhHoc.DTHH { public class HinhChuNhat:HinhVuong { double b; public HinhChuNhat() { } public HinhChuNhat(double a, double b) { this.a = a; this.b = b; } public override double DienTich() { return Math.Round(a*b,2); } public override string ToString() { return "Hinh chu nhat co canh (" + a + "," + b +") dien tich " + this.DienTich(); } } } Nội dung của lớp HinhTron
using System; namespace DoiTuongHinhHoc.DTHH { public class HinhTron:IHinhHoc {
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 69
public double R; public HinhTron() { } public HinhTron(double bankinh) { this.R = bankinh; } public double DienTich() { return Math.Round(Math.PI*R*R,2); } public override string ToString() { return "Duong tron co ban kinh " + R + " dien tich " + this.DienTich(); } } } Nội dung của lớp TongDienTich using System; using System.Collections; using SapXepDoiTuong; namespace DoiTuongHinhHoc.DTHH { public class TongDienTich:IComparer { private ArrayList a = new ArrayList(); public IEnumerator GetEnumerator() { return a.GetEnumerator(); } public TongDienTich() { a.Add(new HinhTron(3)); a.Add(new HinhTron(7)); a.Add(new HinhTron(6)); a.Add(new HinhTamGiac(3,4,5)); a.Add(new HinhVuong(8)); a.Add(new HinhTron(5)); a.Add(new HinhChuNhat(3,4)); } int IComparer.Compare(object a, object b) { IHinhHoc x = (IHinhHoc)a; IHinhHoc y = (IHinhHoc)b; if(x.DienTich() > y.DienTich()) return 1; if(x.DienTich() < y.DienTich()) return -1; return 0; } public void SapXep() { a.Sort(this); } public double TinhTongDienTich()
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 70
{ double tong=0; for(int i=0; i <a.Count; i++) tong += ((IHinhHoc)a[i]).DienTich(); return tong; } public void DemSoLuongCacHinh() { int dem =0; for(int i=0; i <a.Count; i++) if(a[i] is HinhTamGiac) dem++; Console.WriteLine("Co {0} hinh tam good giac trong mang ",dem ); } public override string ToString() { string s=""; for(int i=0; i <a.Count; i++) s += a[i] + "\n"; return s; } } }
Nội dung của lớp Class1(chứa hàm Main để chạy chương trình) using System; using DoiTuongHinhHoc.DTHH; namespace DoiTuongHinhHoc { class Class1 { static void Main(string[] args) { TongDienTich dt = new TongDienTich(); Console.WriteLine("Danh sach cac doi tuong hinh hoc"); Console.WriteLine(dt); //Console.WriteLine("Tong dien tich cua cac hinh " + dt.TinhTongDienTich()); //dt.DemSoLuongCacHinh(); //Console.WriteLine("Danh sach cac hinh theo chieu tang cua dien tich"); // dt.SapXep(); //Console.WriteLine(dt); foreach(IHinhHoc t in dt) Console.WriteLine(t.DienTich()); Console.ReadLine(); } } }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 71
II.Case study 2
Xây dựng chương trình quản lý đăng ký học phần của sinh viên theo các yêu cầu sau:
Hệ thống phải có khả năng lưu trữ thông tin của toàn bộ các sinh viên trong các lớp, danh
sách học phần, danh sách đăng kí học phần.
Quản lý điểm của từng học phần sinh viên đăng kí(giả sử hệ thống không cần lưu trữ
điểm thi lại).
Thống kê kết quả đăng kí học phần theo từng sinh viên hay theo lớp.
Xây dựng các chức năng tìm kiếm thông tin theo sinh viên hay học phần.
Chú ý:
Phần chương trình sau chỉ thiết kế các phương thức cơ bản, sinh viên cần bổ sung thêm
các dữ liệu và các phương thức khác để hoàn chỉnh ứng dụng.
Chương trình lưu trữ thông tin trong mảng một chiều, không sử dụng kiểu Collection để
lưu trữ. Sinh viên có thể bằng cách dùng ArrayList để lưu trữ thông tin của Lớp, Sinh viên
và danh sách học phần nhằm tăng cường sự linh động của ứng dụng.
Mô hình hóa ứng dụng thành lược đồ lớp như sau:
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 72
Nội dung của lớp học phần
using System; namespace QuanLySinhVien { public class HocPhan { public string maHocPhan; public string ten; public int stc; public HocPhan() { } public HocPhan(string maHocPhan, string ten, int stc)
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 73
{ this.maHocPhan = maHocPhan; this.ten = ten; this.stc = stc; } public override string ToString() { return this.maHocPhan + " " + this.ten + "\t\t" + this.stc; } } } Nội dung của lớp DanhSachHocPhan
using System; namespace QuanLySinhVien { public class DanhSachHocPhan { HocPhan []ds= new HocPhan[100]; public int soluong; public HocPhan this[int index] { get { if(index>=0 && index< soluong) return ds[index]; else return null; } set { ds[index] = value; } } public HocPhan this[string mahp] { get { return ds[TimViTriTheoMaHocPhan(mahp)]; } } public DanhSachHocPhan() { for(int i=0; i< 100; i++) this[i] = new HocPhan(); this[0] = new HocPhan("HP001","Lap trinh 1",3); this[1] = new HocPhan("HP002","Thuc hanh LT 1",2); this[2] = new HocPhan("HP003","Cau Truc Du Lieu",3); this[3] = new HocPhan("HP004","Nhap Mon Tin Hoc",5); this.soluong = 4; } public int TimViTriTheoMaHocPhan(string mahp) { for(int i =0; i < this.soluong; i++) if(this[i].maHocPhan.CompareTo(mahp)==0) return i;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 74
return -1; } } }
Nội dung của lớp SinhVien
using System; namespace QuanLySinhVien { [Serializable] public class SinhVien { public delegate void SuKienThayDoiTen(); public event QuanLySinhVien.SinhVien.SuKienThayDoiTen ThayDoiTen; public string maso; private string ten; public string Ten { set { ten = value; KhiTenThayDoi(); } get { return ten; } } public bool gioitinh; public virtual void KhiTenThayDoi() { if(ThayDoiTen!=null) ThayDoiTen(); } public SinhVien() { } public void Nhap() { Console.Write("Nhap maso "); maso = Console.ReadLine(); Console.Write("Nhap ten "); ten = Console.ReadLine(); Console.Write("Nhap gioi tinh 1 cho Nam, 0 cho Nu "); int i = int.Parse(Console.ReadLine()); if(i==1) this.gioitinh = true; else this.gioitinh = false;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 75
} public SinhVien(string maso, string ten, bool gioitinh) { this.maso = maso; this.ten = ten; this.gioitinh = gioitinh; } public string GT(bool gt) { return gt?"Nam":"Nu"; } public override string ToString() { return maso + "\t" + ten + "\t" + GT(this.gioitinh); } public bool CapNhat(string maso, string ten, bool gioitinh) { this.maso = maso; this.ten = ten; this.gioitinh = gioitinh; return true; } } } Nội dung của lớp LopSinhVien
using System; using System.Collections; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization; namespace QuanLySinhVien { public class LopSinhVien { private ArrayList ds = new ArrayList(); public SinhVien this[int index] { get { if(index>=0 && index< ds.Count) return (SinhVien)ds[index]; else return null; } set { ds[index] = value; }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 76
} public SinhVien this[string maso] { get { int vt = TimViTriTheoMaSo(maso); if(vt>=0) return (SinhVien)ds[vt]; else return null; } } public LopSinhVien() { /* ds.Add(new SinhVien("001","Thanh",true)); ds.Add(new SinhVien("002","Tuyet",false)); ds.Add(new SinhVien("003","Phung",false)); ds.Add(new SinhVien("004","Anh",true)); */ DocFileDSSV(); } public void Add(SinhVien a ) { ds.Add(a); } public void DocFileDSSV() { Stream s = File.Open("C:\\Data\\dssv.dat",FileMode.Open); IFormatter ib = new BinaryFormatter(); ds = (ArrayList)ib.Deserialize(s); s.Close(); //Console.WriteLine("Doc file C:\\Data\\dssv.dat thanh cong "); } public void LuuDSSV() { Stream s = File.Open("C:\\Data\\dssv.dat",FileMode.Create); IFormatter ib = new BinaryFormatter(); ib.Serialize(s,ds); s.Close(); //Console.WriteLine("Luu danh sach sinh vien xuong file C:\\Data\\dssv.dat thanh cong "); } public void NhapDanhSachLopSinhVien() { int n; Console.Write("\nNhap vao so luong can nhap "); n = int.Parse(Console.ReadLine()); for( int i = 0; i< n; i++) { Console.WriteLine("Nhap sinh vien thu {0} ", ds.Count +i +1); this[ds.Count + i].Nhap();
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 77
} } private bool XoaTaiViTri(int index) { if(index < 0 || index > ds.Count) return false; for(int i = index; i < this.ds.Count -1; i++) this[i] = this[i + 1]; //this.this[ds.Count-1] = null; //ds.Count--; return true; } private int TimViTriTheoMaSo(string maso) { for(int i =0; i < this.ds.Count; i++) if(this[i].maso.CompareTo(maso)==0) return i; return -1; } public bool XoaTheoMaSo(string maso) { return XoaTaiViTri(TimViTriTheoMaSo(maso)); } public override string ToString() { string s= ""; for( int i=0; i< ds.Count; i++) s += "\n" + this[i]; return s; } } } Nội dung của lớp DangKyHocPhan
using System; namespace QuanLySinhVien { public class DangKyHocPhan { public string masv; public string mahp; public float diem; public DangKyHocPhan() { } public DangKyHocPhan(string masomasv, string mamahp) { this.masv = masomasv;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 78
this.mahp = mamahp; } public DangKyHocPhan(SinhVien masv, HocPhan mahp) { this.masv = masv.maso; this.mahp = mahp.maHocPhan; } public DangKyHocPhan(SinhVien masv, HocPhan mahp, float diem) { this.masv = masv.maso; this.mahp = mahp.maHocPhan; this.diem = diem; } } } Nội dung của lớp DSDangKyHocPhan
using System; namespace QuanLySinhVien { public class DSDangKyHocPhan { public LopSinhVien k28 = new LopSinhVien(); DanhSachHocPhan hp = new DanhSachHocPhan(); DangKyHocPhan []ds = new DangKyHocPhan[100]; public int soluong; public DangKyHocPhan this[int index] { get { if(index>=0 && index< soluong) return ds[index]; else return null; } set { ds[index] = value; } } public DSDangKyHocPhan() { for(int i =0; i < 100; i++) this[i] = new DangKyHocPhan(); this[0] = new DangKyHocPhan(k28[0],hp[0],7); this[1] = new DangKyHocPhan(k28[0],hp[1]); this[2] = new DangKyHocPhan(k28[0],hp[2],3); this[3] = new DangKyHocPhan(k28[2],hp[0],4); this[4] = new DangKyHocPhan(k28[3],hp[3]); this[5] = new DangKyHocPhan(k28[3],hp[1],8); this[6] = new DangKyHocPhan(k28[3],hp[2]); this[7] = new DangKyHocPhan(k28[3],hp[0]); this[8] = new DangKyHocPhan(k28[0],hp[3]); soluong = 9;
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 79
} public string DSDangKyHocPhanTheoMon(string mahp) { string s = ""; for(int i=0; i < soluong; i++) if(ds[i].mahp.CompareTo(mahp)==0) s+= k28[ds[i].masv] + "\t" + hp[ds[i].mahp] + "\t" +ds[i].diem+ "\n"; return s; } public void ThongKeSoLuongDangKyHocPhan() { for(int i=0; i< this.hp.soluong; i++) { int dem =0; for(int j=0; j < this.soluong; j++) { if(this.hp[i].maHocPhan.CompareTo(this[j].mahp)==0) { dem++; } } Console.WriteLine("Co {0} sinh vien dang ki hoc phan {1}",dem,this.hp[i].ten); } } public void DanhSachDangKyHocPhanCua1SV(string maso) { SinhVien x = this.k28[maso]; if(x==null) { Console.WriteLine("Khong co sinh vien co ma so " + maso + " trong danh sach"); return; } Console.WriteLine("KET QUA DANG KY HOC PHAN "); Console.WriteLine("Ma So: " + maso); Console.WriteLine("Ten: " + x.Ten); Console.WriteLine("Gioi Tinh: " + x.GT(x.gioitinh)); int tongtc =0; string s=""; for(int i=0; i<this.soluong; i++) { if(maso.CompareTo(this[i].masv)==0) { tongtc += this.hp[this[i].mahp].stc; s += this.hp[this[i].mahp] + "\n"; } } if(s.Length==0) { Console.WriteLine("Sinh vien chua dang ky hoc phan" );
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 80
return; } Console.WriteLine(s); Console.WriteLine("Tong so tin chi dang ki la " + tongtc); } public override string ToString() { string s =""; for(int i=0; i < soluong; i++) s+= k28[ds[i].masv] + "\t" + hp[ds[i].mahp] + "\t" +ds[i].diem+ "\n"; return s; } } } Nội dung của lớp Class1(dùng để thực hiện kiểm tra các chức năng trong chương trình)
using System; namespace QuanLySinhVien { class Class1 { static void Main(string[] args) { /* LopSinhVien k28 = new LopSinhVien(); Console.WriteLine("\n In danh sach LopSinhVien K28 "); Console.Write(k28); Console.ReadLine(); */ DSDangKyHocPhan ql = new DSDangKyHocPhan(); Console.WriteLine("\n In danh sach LopSinhVien K28 "); Console.Write(ql.k28 + "\n\n"); Console.WriteLine(ql); Console.WriteLine("\nDanh sach dang ki mon hoc lap trinh 1"); Console.WriteLine(ql.DSDangKyHocPhanTheoMon("HP001")); /* DSDangKyHocPhan ql2 = new DSDangKyHocPhan(); Console.WriteLine(ql2); Console.WriteLine("\nDanh sach dang ki mon hoc Thuc Hanh"); Console.WriteLine(ql2.DSDangKyHocPhanTheoMon("HP003")); */ //Console.WriteLine("Thong ke so luong sinh vien dang ky hoc phan "); //ql.ThongKeSoLuongDangKyHocPhan(); //ql.DanhSachDangKyHocPhanCua1SV("001"); //ql.DanhSachDangKyHocPhanCua1SV("002"); //LopSinhVien k28 = new LopSinhVien(); //k28.DocFileDSSV(); //Console.WriteLine(k28); //k28.Add(new SinhVien("005","Moi them ", true)); //Console.WriteLine(k28); //k28.LuuDSSV(); Console.ReadLine(); }
Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006
Trang 81
} }