81
Lp trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006 Trang 1 Hướng dn hc môn Nguyên lý lp trình 2 và Thc hành nguyên lý lp trình 2 Đánh giá: Thi máy gia kì(30% đim) cho môn nguyên lý lp trình 2. Thi viết cui kì(70% đim) cho môn nguyên lý lp trình 2. Thi máy cui kì(100% đim) cho môn thc hành nguyên lý lp trình 2. Có thêm các bài kim tra trong quá trình thc hành và cng đim vào bài thi gia kì hoc cui kì. Tài liu tham kho Bài ging môn nguyên lý lp trình 2. Download eBook “Wrox Press - Professional C#, 3rd Edition.pdf ” ti đường dn sau \\192.168.6.1\cisco\Downloads . MSDN-Microsoft Development Networks Website: http://www.codeproject.com . Mt ssourcecode mu được cung cp trong quá trình hc. Thc hành Bao gm 5 bài lab. Download bài lab ti địa chhttp://192.168.6.1/NLLT2/labX.htm . Trong đó X là thtbài lab. Ngôn ngminh ha cho lp trình hướng đối tượng: C#(C Sharp). Môi trường thc hành Visual Studio 2003 hoc 2005.

baiGiangNLLT2(moi)

Embed Size (px)

Citation preview

Page 1: baiGiangNLLT2(moi)

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.

Page 2: baiGiangNLLT2(moi)

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:

Page 3: baiGiangNLLT2(moi)

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.

Page 4: baiGiangNLLT2(moi)

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ể

Page 5: baiGiangNLLT2(moi)

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();

Page 6: baiGiangNLLT2(moi)

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.

Page 7: baiGiangNLLT2(moi)

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");

Page 8: baiGiangNLLT2(moi)

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..

Page 9: baiGiangNLLT2(moi)

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

Page 10: baiGiangNLLT2(moi)

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

Page 11: baiGiangNLLT2(moi)

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.

Page 12: baiGiangNLLT2(moi)

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); }

Page 13: baiGiangNLLT2(moi)

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 );

Page 14: baiGiangNLLT2(moi)

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());

Page 15: baiGiangNLLT2(moi)

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 ) {

Page 16: baiGiangNLLT2(moi)

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:

Page 17: baiGiangNLLT2(moi)

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

Page 18: baiGiangNLLT2(moi)

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";

Page 19: baiGiangNLLT2(moi)

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( )

Page 20: baiGiangNLLT2(moi)

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; } } } }

Page 21: baiGiangNLLT2(moi)

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.

Page 22: baiGiangNLLT2(moi)

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

Page 23: baiGiangNLLT2(moi)

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;

Page 24: baiGiangNLLT2(moi)

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

Page 25: baiGiangNLLT2(moi)

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 ");

Page 26: baiGiangNLLT2(moi)

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;

Page 27: baiGiangNLLT2(moi)

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.

Page 28: baiGiangNLLT2(moi)

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; }

Page 29: baiGiangNLLT2(moi)

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

Page 30: baiGiangNLLT2(moi)

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) {

Page 31: baiGiangNLLT2(moi)

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)

Page 32: baiGiangNLLT2(moi)

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]; }

Page 33: baiGiangNLLT2(moi)

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

Page 34: baiGiangNLLT2(moi)

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.

Page 35: baiGiangNLLT2(moi)

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.

Page 36: baiGiangNLLT2(moi)

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.

Page 37: baiGiangNLLT2(moi)

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.

Page 38: baiGiangNLLT2(moi)

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;

Page 39: baiGiangNLLT2(moi)

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.

Page 40: baiGiangNLLT2(moi)

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.

Page 41: baiGiangNLLT2(moi)

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); }

Page 42: baiGiangNLLT2(moi)

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( ); } }

Page 43: baiGiangNLLT2(moi)

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; }

Page 44: baiGiangNLLT2(moi)

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( ); } } }

Page 45: baiGiangNLLT2(moi)

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( ) {

Page 46: baiGiangNLLT2(moi)

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ị.

Page 47: baiGiangNLLT2(moi)

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; }

Page 48: baiGiangNLLT2(moi)

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 {

Page 49: baiGiangNLLT2(moi)

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) {

Page 50: baiGiangNLLT2(moi)

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();

Page 51: baiGiangNLLT2(moi)

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

Page 52: baiGiangNLLT2(moi)

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 {

Page 53: baiGiangNLLT2(moi)

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.

Page 54: baiGiangNLLT2(moi)

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

Page 55: baiGiangNLLT2(moi)

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");

Page 56: baiGiangNLLT2(moi)

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( );

Page 57: baiGiangNLLT2(moi)

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( ) {

Page 58: baiGiangNLLT2(moi)

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) {

Page 59: baiGiangNLLT2(moi)

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");

Page 60: baiGiangNLLT2(moi)

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;

Page 61: baiGiangNLLT2(moi)

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"); }

Page 62: baiGiangNLLT2(moi)

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 {

Page 63: baiGiangNLLT2(moi)

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;

Page 64: baiGiangNLLT2(moi)

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 {

Page 65: baiGiangNLLT2(moi)

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 {

Page 66: baiGiangNLLT2(moi)

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 {

Page 67: baiGiangNLLT2(moi)

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 {

Page 68: baiGiangNLLT2(moi)

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 {

Page 69: baiGiangNLLT2(moi)

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()

Page 70: baiGiangNLLT2(moi)

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(); } } }

Page 71: baiGiangNLLT2(moi)

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:

Page 72: baiGiangNLLT2(moi)

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)

Page 73: baiGiangNLLT2(moi)

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;

Page 74: baiGiangNLLT2(moi)

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;

Page 75: baiGiangNLLT2(moi)

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; }

Page 76: baiGiangNLLT2(moi)

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();

Page 77: baiGiangNLLT2(moi)

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;

Page 78: baiGiangNLLT2(moi)

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;

Page 79: baiGiangNLLT2(moi)

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" );

Page 80: baiGiangNLLT2(moi)

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(); }

Page 81: baiGiangNLLT2(moi)

Lập trình hướng đối tượng – Tran Thong - Khoa CNTT-DHDL 2006

Trang 81

} }