Professional Documents
Culture Documents
GIÁO TRÌNH
Mô đun: LẬP TRÌNH C#.NET
NGHỀ: CÔNG NGHỆ THÔNG TIN
(ỨNG DỤNG PHẦN MỀM)
TRÌNH ĐỘ CAO ĐẲNG NGHỀ
GIÁO TRÌNH
Mô đun: LẬP TRÌNH C#.NET
NGHỀ: CÔNG NGHỆ THÔNG TIN
(ỨNG DỤNG PHẦN MỀM)
TRÌNH ĐỘ CAO ĐẲNG NGHỀ
Ban hành kèm theo Quyết định số: /QĐ-CĐN ngày ..... tháng.... năm 2014
2.6.2 Tạo và thực thi chƣơng trình C# với Windows Form Application ..........21
a. Cú pháp: ..........................................................................................................29
4.4.2. Ví dụ .........................................................................................................47
5.3 Phƣơng thức tạo lập (constructor) của một đối tƣợng .....................................62
5.4 Phƣơng thức tạo lập sao chép (copy constructor) ............................................63
5.5 Sử dụng các thành viên tĩnh (static member)...................................................64
6.1 Sự kế thừa.........................................................................................................77
6.1.5. Định nghĩa phiên bản mới trong lớp dẫn xuất ..........................................81
7.2 Kiểm tra đối tƣợng có hỗ trợ giao diện hay không bằng toán tử is ...............101
7.3 Thực thi các giao diện tƣờng minh: Icomparer, IComparable (giao diện so
sánh) và ArrayList ................................................................................................101
8.1.1 Sử dụng ủy quyền để xác nhận phƣơng thức lúc thực thi .......................108
8.2 Sự kiện............................................................................................................130
2
MÔ ĐUN LẬP TRÌNH C#.NET
Mã mô đun: MĐ 33
Vị trí, tính chất, ý nghĩa và vai trò của mô đun:
-Lập trình trên Windows là mô đun tự chọn thuộc chuyên môn nghề của
chƣơng trình đào tạo Cao đẳng nghề (ứng dụng phần mềm). Mô đun này đƣợc bố trí
giảng sau các môn cơ sở ngành.
-Là mô đun lập trình cơ sở trên nền lập trình .NET FRAMEWORK, để làm
nền tảng cho việc tiếp cận lập trình các ứng dụng .NET và lập trình ASP.NET.
Mục tiêu của mô đun:
Trình bày đƣợc các kiến thức về nền tảng Microsoft .NET.
Vận dụng đƣợc các kỹ thuật lập trình cơ bản trong ngôn ngữ C#
Có kiến thức và kỹ năng xử lý mảng, chuỗi;
Vận dụng các kiến thức và kỹ năng về lập trình hƣớng đối tƣợng trên C#.
Có kiến thức và kỹ năng về giao diện trong C#.
Có kiến thức và kỹ năng về cơ chế uỷ quyền;
Tạo đƣợc các ứng dụng trên windows sử dụng ngôn ngữ C# trên môi trƣờng
.Net;
Nghiêm túc, tỉ mỉ trong việc tiếp nhận kiến thức. Chủ động, tích cực trong thực
hành và tìm kiếm nguồn bài tập liên quan.
Nội dung của mô đun:
Thời gian (giờ)
Số
Tên chƣơng, mục Tổng Lý Thực Kiểm
TT
số thuyết hành tra
1 Bài 1: Giới thiệu về Microsoft .NET 2 2 0 0
2 Bài 2: Cơ bản ngôn ngữ C# 6 3 3 0
Bài 3: Các cấu trúc điều khiển trong
3 12 3 8 1
ngôn ngữ C#
Bài 4: Mảng (Array) – Chuỗi (String)
4 18 6 11 1
– Xử lý ngoại lệ (Exception)
Bài 5: Lớp (Class) – Đối tƣợng
5 18 6 11 1
(Object) -Phƣơng thức (Method)
3
6 Bài 6: Kế thừa và Đa hình 14 3 9 1
7 Bài 7: Thực thi giao diện 6 3 3 0
8 Bài 8: Cơ chế uỷ quyền và sƣ kiện 5 2 3 0
9 Bài 9: Các lớp cơ sở .NET 9 2 5 0
10 Cộng 90 30 56 4
4
BÀI 1
GIỚI THIỆU MICROSOFT .NET
Mã bài: MĐ33-01
Giới thiệu:
Microsoft .Net là một công nghệ lập trình mới đƣợc thực thi trên nền .NET
Framework của hãng Microsoft.
Thành phần Framework là quan trọng nhất .NET là cốt lõi và tinh hoa của môi
trƣờng lập trình trên thƣ viện CLR, còn IDE chỉ là công cụ để phát triển dựa trên
nền tảng đó thôi. Trong .NET toàn bộ các ngôn ngữ C#, Visual C++ hay Visual
Basic.NET đều dùng cùng một IDE.
Tóm lại Microsoft .NET là nền tảng cho việc xây dựng và thực thi các ứng dụng
phân tán thế hệ kế tiếp. Bao gồm các ứng dụng từ client đến server và các dịch vụ
khác. Một số tính năng của Microsoft .NET cho phép những nhà phát triển sử dụng
nhƣ sau:
□ Một mô hình lập trình cho phép nhà phát triển xây dựng các ứng dụng dịch
vụ web và ứng dụng client với Extensible Markup Language (XML).
□ Tập hợp dịch vụ XML Web, nhƣ Microsoft .NET My Services cho phép nhà
phát triển đơn giản và tích hợp ngƣời dùng kinh nghiệm.
□ Cung cấp các server phục vụ bao gồm: Windows 2000, SQL Server, và
BizTalk Server, tất cả điều tích hợp, hoạt động, và quản lý các dịch vụ XML
Web và các ứng dụng.
□ Các phần mềm client nhƣ Windows XP và Windows CE giúp ngƣời phát
triển phân phối sâu và thuyết phục ngƣời dùng kinh nghiệm thông qua các
dòng thiết bị.
□ Nhiều công cụ hỗ trợ nhƣ Visual Studio .NET, để phát triển các dịch vụ Web
XML, ứng dụng trên nền Windows hay nền web một cách dể dàng và hiệu
quả.
Mục tiêu:
Trình bày đƣợc các kiến thức nền tảng Microsoft.Net;
Sử dụng cơ chế trình biên dịch MSIL;
Thao tác đƣợc trên môi trƣờng lập trình C#.Net;
Nghiêm túc, tỉ mỉ trong quá trình tiếp cận với môi trƣờng lập trình mới.
Nội dung chính:
5
1.1 Nguồn gốc .NET
Đầu năm 1998, sau khi hoàn tất phiên bản Version 4 của Internet Information
Server (IIS), các đội ngũ lập trình ở Microsoft nhận thấy họ còn rất nhiều sáng kiến
để kiện toàn IIS. Họ bắt đầu xây dựng một kiến trúc mới trên nền tảng ý tƣởng đó
và đặt tên là Next Generation Windows Services (NGWS).
Sau khi Visual Basic đƣợc trình làng vào cuối 1998, dự án kế tiếp mang tên
Visual Studio 7 đƣợc xác nhập vào NGWS. Đội ngũ COM+/MTS góp vào một
universal runtime cho tất cả ngôn ngữ lập trình chung trong Visual Studio, và tham
vọng của họ cung cấp cho các ngôn ngữ lập trình của các công ty khác dùng chung
luôn. Công việc này đƣợc xúc tiến một cách hoàn toàn bí mật mãi cho đến hội nghị
Professional Developers‟ Conference ở Orlado vào tháng 7/2000. Đến tháng
11/2000 thì Microsoft đã phát hành bản Beta 1 của .NET gồm 3 đĩa CD. Tính đến
lúc này thì Microsoft đã làm việc với .NET gần 3 năm rồi, do đó bản Beta 1 này
tƣơng đối vững chắc.
.NET mang dáng dấp của những sáng kiến đã đƣợc áp dụng trƣớc đây nhƣ p-
code trong UCSD Pascal cho đến Java Virtual Machine. Có điều là Microsoft góp
nhặt những sáng kiến của ngƣời khác, kết hợp với sáng kiến của chính mình để làm
nên một sản phẩm hoàn chỉnh từ bên trong lẫn bên ngoài. Hiện tại Microsoft đã
công bố phiên bản release của .NET.
Thật sự Microsoft đã đặt cƣợc vào .NET vì theo thông tin của công ty, đã tập
trung 80% sức mạnh của Microsoft để nghiên cứu và triển khai .NET (bao gồm
nhân lực và tài chính ?), tất cả các sản phẩm của Microsoft sẽ đƣợc chuyển qua
.NET.
1.2 Tổng quan Microsoft .NET
Microsoft .NET gồm 2 phần chính : Framework và Integrated Development
Environment (IDE). Framework cung cấp những gì cần thiết và căn bản, chữ
Framework có nghĩa là khung hay khung cảnh trong đó ta dùng những hạ tầng cơ
sở theo một qui ƣớc nhất định để công việc đƣợc trôi chảy. IDE thì cung cấp một
môi trƣờng giúp chúng ta triển khai dễ dàng, và nhanh chóng các ứng dụng dựa trên
nền tảng .NET. Nếu không có IDE chúng ta cũng có thể dùng một trình soạn thảo
ví nhƣ Notepad hay bất cứ trình soạn thảo văn bản nào và sử dụng command line để
biên dịch và thực thi, tuy nhiên việc này mất nhiều thời gian. Tốt nhất là chúng ta
dùng IDE phát triển các ứng dụng, và cũng là cách dễ sử dụng nhất.
Thành phần Framework là quan trọng nhất .NET là cốt lõi và tinh hoa của môi
trƣờng, còn IDE chỉ là công cụ để phát triển dựa trên nền tảng đó thôi. Trong .NET
toàn bộ các ngôn ngữ C#, Visual C++ hay Visual Basic.NET đều dùng cùng một
IDE.
6
Tóm lại Microsoft .NET là nền tảng cho việc xây dựng và thực thi các ứng dụng
phân tán thế hệ kế tiếp. Bao gồm các ứng dụng từ client đến server và các dịch vụ
khác. Một số tính năng của Microsoft .NET cho phép những nhà phát triển sử dụng
nhƣ sau:
□ Một mô hình lập trình cho phép nhà phát triển xây dựng các ứng dụng dịch
vụ web và ứng dụng client với Extensible Markup Language (XML).
□ Tập hợp dịch vụ XML Web, nhƣ Microsoft .NET My Services cho phép nhà
phát triển đơn giản và tích hợp ngƣời dùng kinh nghiệm.
□ Cung cấp các server phục vụ bao gồm: Windows 2000, SQL Server, và
BizTalk Server, tất cả điều tích hợp, hoạt động, và quản lý các dịch vụ XML
Web và các ứng dụng.
□ Các phần mềm client nhƣ Windows XP và Windows CE giúp ngƣời phát
triển phân phối sâu và thuyết phục ngƣời dùng kinh nghiệm thông qua các
dòng thiết bị.
□ Nhiều công cụ hỗ trợ nhƣ Visual Studio .NET, để phát triển các dịch vụ Web
XML, ứng dụng trên nền Windows hay nền web một cách dể dàng và hiệu
quả.
1.3 Kiến trúc .NET Framework
.NET Framework là một platform mới làm đơn giản việc phát triển ứng dụng
trong môi trƣờng phân tán của Internet. .NET Framework đƣợc thiết kế đầy đủ để
đáp ứng theo quan điểm sau:
□ Để cung cấp một môi trƣờng lập trình hƣớng đối tƣợng vững chắc, trong đó
mã nguồn đối tƣợng đƣợc lƣu trữ và thực thi một cách cục bộ. Thực thi cục
bộ nhƣng đƣợc phân tán trên Internet, hoặc thực thi từ xa.
□ Để cung cấp một môi trƣờng thực thi mã nguồn mà tối thiểu đƣợc việc đóng
gói phần mềm và sự tranh chấp về phiên bản.
□ Để cung cấp một môi trƣờng thực thi mã nguồn mà đảm bảo việc thực thi an
toàn mã nguồn, bao gồm cả việc mã nguồn đƣợc tạo bởi hãng thứ ba hay bất
cứ hãng nào mà tuân thủ theo kiến trúc .NET.
□ Để cung cấp một môi trƣờng thực thi mã nguồn mà loại bỏ đƣợc những lỗi
thực hiện các script hay môi trƣờng thông dịch.
□ Để làm cho những ngƣời phát triển có kinh nghiệm vững chắc có thể nắm
vững nhiều kiểu ứng dụng khác nhau. Nhƣ là từ những ứng dụng trên nền
Windows đến những ứng dụng dựa trên web.
7
□ Để xây dựng tất cả các thông tin dựa triên tiêu chuẩn công nghiệp để đảm
bảo rằng mã nguồn trên .NET có thể tích hợp với bất cứ mã nguồn khác.
.NET Framework có hai thành phần chính: Common Language Runtime (CLR) và
thƣ viện lớp .NET Framework. CLR là nền tảng của .NET Framework. Chúng ta có
thể hiểu runtime nhƣ là một agent quản lý mã nguồn khi nó đƣợc thực thi, cung cấp
các dịch vụ cốt lõi nhƣ: quản lý bộ nhớ, quản lý tiểu trình, và quản lý từ xa. Ngoài
ra nó còn thúc đẩy việc sử dụng kiểu an toàn và các hình thức khác của việc chính
xác mã nguồn, đảm bảo cho việc thực hiện đƣợc bảo mật và mạnh mẽ. Thật vậy,
khái niệm quản lý mã nguồn là nguyên lý nền tảng của runtime. Mã nguồn mà đích
tới runtime thì đƣợc biết nhƣ là mã nguồn đƣợc quản lý (managed code). Trong khi
đó mã nguồn mà không có đích tới runtime thì đƣợc biết nhƣ mã nguồn không đƣợc
quản lý (unmanaged code).
Thƣ viện lớp, một thành phần chính khác của .NET Framework là một tập
hợp hƣớng đối tƣợng của các kiểu dữ liệu đƣợc dùng lại, nó cho phép chúng ta có
thể phát triển những ứng dụng từ những ứng dụng truyền thống command-line hay
những ứng dụng có giao diện đồ họa (GUI) đến những ứng dụng mới nhất đƣợc
cung cấp bởi ASP.NET, nhƣ là Web Form và dịch vụ XML Web.
8
Tập hợp các ngôn ngữ : C#, VB.Net, J#, F#, VC++…
Công cụ phát triển Visual Studio
Lớp đặc tả ngôn ngữ dùng chung (CLS)
Các thƣ viện để phát triển ứng dụng
Bộ thực thi ngôn ngữ dùng chung (CLR)
Chƣơng trình không biên dịch thành tập tin thực thi, mà biên dịch thành ngôn
ngữ trung gian IL (Intermediate Language-IL), sau đó đƣợc CLR (Common
Language Runtime) thực thi.
9
BÀI 2
CƠ BẢN NGÔN NGỮ C#
Mã bài: MĐ33-02
Giới thiệu:
Khi tìm hiểu bất kỳ ngôn ngữ nào ta cũng cần hiểu các đặc trƣng của ngôn
ngữ đó nhƣ bảng chữ cái, từ, cấu tạo câu... Ví dụ khi học về ngôn ngữ tiếng Anh ta
cần biết bảng chữ cái, các từ và cấu trúc câu trong tiếng Anh nhƣ thế nào? Với ngôn
ngữ lập trình nói chung cũng vậy và ngôn ngữ lập trình C#.NET nói riêng ta cũng
cần tìm hiểu những đặc trƣng trên ngôn ngữ này là gì? Để có thể làm việc tốt với
ngôn ngữ C# ta cần tìm hiểu từ khoá, kiểu dữ liệu, cấu trúc điều khiển trong ngôn
ngữ này đƣợc khai báo và sử dụng nhƣ thế nào.
Ngôn ngữ C# khá đơn giản, chỉ khoảng hơn 80 từ khóa và hơn mƣời mấy
kiểu dữ liệu đƣợc dựng sẵn. Tuy nhiên, ngôn ngữ C# có ý nghĩa to lớn khi nó thực
thi những khái niệm lập trình hiện đại. C# bao gồm tất cả những hỗ trợ cho cấu trúc,
thành phần component, lập trình hƣớng đối tƣợng. Những tính chất đó hiện diện
trong một ngôn ngữ lập trình hiện đại. Hơn nữa ngôn ngữ C# đƣợc xây dựng trên
nền tảng hai ngôn ngữ mạnh nhất là C++ và Java.
Mục tiêu:
Trình bày đƣợc các kiến thức và chức năng tiên tiến trên C#;
Vận dụng đƣợc các kiểu dữ liệu dựng sẵn của C#;
Vận dụng đƣợc các cơ chế thực thi các biến, hằng và các biểu thức trên C#;
Vận dụng đƣợc các cấu trúc điều khiển trong ngôn ngữ C# để giải các bài toán
theo yêu cầu đặt ra.
Trình bày đƣợc kiến thức về không gian tên (Namespace);
Trình bày đƣợc kiến thức về các toán tử;
Trình bày đƣợc kiến thức về chỉ dẫn biên dịch;
Tạo và thực thi đƣợc ứng dụng đơn giản trên C#;
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
2.1 Giới thiệu về ngôn ngữ C#
Ngôn ngữ C# khá đơn giản, chỉ khoảng hơn 80 từ khóa và hơn mƣời mấy kiểu
10
dữ liệu đƣợc dựng sẵn. Tuy nhiên, ngôn ngữ C# có ý nghĩa to lớn khi nó thực thi
những khái niệm lập trình hiện đại. C# bao gồm tất cả những hỗ trợ cho cấu trúc,
thành phần component, lập trình hƣớng đối tƣợng. Những tính chất đó hiện diện
trong một ngôn ngữ lập trình hiện đại. Hơn nữa ngôn ngữ C# đƣợc xây dựng trên
nền tảng hai ngôn ngữ mạnh nhất là C++ và Java.
Tóm lại, C# có các đặc trƣng sau đây:
C# là ngôn ngữ đơn giản
C# là ngôn ngữ hiện đại
C# là ngôn ngữ hƣớng đối tƣợng
C# là ngôn ngữ mạnh mẽ và mềm dẻo
C# là ngôn ngữ hƣớng module
Hoạt động trên .NET Framework.
Dựa trên phƣơng pháp thiết kế hƣớng đối tƣợng.
Dùng cho cả 3 loại ứng dụng: Console, Winform, Webform.
Có tính diễn đạt ngữ nghĩa cao.
Phân biệt chữ hoa thƣờng.
2.1.1. C# là ngôn ngữ đơn giản
- C# loại bỏ đƣợc một vài sự phức tạp và rối rắm của các ngôn ngữ C++ và
Java.
- C# khá giống C / C++ về diện mạo, cú pháp, biểu thức, toán tử.
- Các chức năng của C# đƣợc lấy trực tiếp từ ngôn ngữ C / C++ nhƣng đƣợc
cải tiến để làm cho ngôn ngữ đơn giản hơn.
2.1.2. C# là ngôn ngữ hiện đại
C# có đƣợc những đặc tính của ngôn ngữ hiện đại nhƣ:
- Xử lý ngoại lệ
- Thu gom bộ nhớ tự động
- Có những kiểu dữ liệu mở rộng - Bảo mật mã nguồn
2.1.3. C# là ngôn ngữ hƣớng đối tƣợng
C# hỗ trợ tất cả những đặc tính của ngôn ngữ hƣớng đối tƣợng là: - Sự đóng
gói (encapsulation)
- Sự kế thừa (inheritance) - Đa hình (polymorphism)
11
2.1.4. C# là ngôn ngữ mạnh mẽ và mềm dẻo
- Với ngôn ngữ C#, chúng ta chỉ bị giới hạn ở chính bản thân của chúng ta.
Ngôn ngữ này không đặt ra những ràng buộc lên những việc có thể làm.
- C# đƣợc sử dụng cho nhiều dự án khác nhau nhƣ: tạo ra ứng dụng xử lý văn
bản, ứng dụng đồ họa, xử lý bảng tính; thậm chí tạo ra những trình biên dịch cho
các ngôn ngữ khác.
- C# là ngôn ngữ sử dụng giới hạn những từ khóa. Phần lớn các từ khóa dùng
để mô tả thông tin, nhƣng không gì thế mà C# kém phần mạnh mẽ. Chúng ta có
thể tìm thấy rằng ngôn ngữ này có thể đƣợc sử dụng để làm bất cứ nhiệm vụ nào.
2.1.5. C# là ngôn ngữ hƣớng module
- Mã nguồn của C# đƣợc viết trong Class (lớp). Những Class này chứa các
Method (phƣơng thức) thành viên của nó.
- Class (lớp) và các Method (phƣơng thức) thành viên của nó có thể đƣợc sử
dụng lại trong những ứng dụng hay chƣơng trình khác.
2.1.6. C# sẽ trở nên phổ biến
- C# mang đến sức mạnh của C++ cùng với sự dễ dàng của ngôn ngữ Visual
Basic.
2.2 Kiểu dữ liệu
C# chia kiểu dữ liệu thành hai tập hợp kiểu dữ liệu chính:
- Kiểu xây dựng sẵn (built-in): do ngôn ngữ cung cấp cho ngƣời lập trình. -
Kiểu do ngƣời dùng định nghĩa (user-defined): do ngƣời lập trình tạo ra.
2.2.1. Kiểu dữ liệu dựng sẵn
Kiểu số
Kiểu C# Số byte Kiểu .NET Mô tả
byte 1 Byte Số nguyên dƣơng không dấu từ 0 đên
char 2 Char Ký tự Unicode 255
bool 1 Boolean Giá trị logic true / false
sbyte 1 Sbyte Số nguyên có dấu từ -128 đên 127
short 2 Int16 Số nguyên có dấu từ -32768 đên 32767
ushort 2 Uint16 Số nguyên dƣơng không dấu từ 0 đên
int 4 Int32 Số nguyên có dấu65535
từ -2.147.483.647 đên
uint 4 Uint32 Số nguyên không dấu từ 0 đên
2.147483.647
float 4 Single 4.294.967.295
Kiểu dấu chấm động, giá trị xấp xỉ từ -
12 đên 3.4E+38, với 7 chữ số có
3.4E-38
nghĩa
double 8 Double Kiểu dấu chấm động có độ chính xác
decimal 8 Decimal gấp
Có độđôi, giá xác
chính trị xấp
đênxỉ28từcon số và giáđên
-1.7E-308 trị
long 8 Int64 1.7E+308,
thập
Kiểu phân, với
đƣợc
số nguyên 15,dùng
có16dấu
chữ số giá
trong
có có nghĩa
tính toán
trị tài
trong
ulong 8 Uint64 chính,
khoảng kiểu
Số nguyên này đòi hỏi phảitừcó 0hậuđên
-9.223.370.036.854.775.808
không dấu tố
đên
“m” hay “M”
9.223.372.036.854.775.807
0xfffffffffffffff
Kiểu chuỗi
C# định nghĩa kiểu string nhƣ một kiểu dữ liệu cơ bản (khác với C, C++)
Kiểu string có thể chứa nội dung không giới hạn, vì đây là kiểu dữ liệu đối
tƣợng đƣợc chứa ở bộ nhớ heap.
Khai báo : string s = “Nguyen van a”;
Kiểu mảng
Mảng là một tập hợp các phần tử cùng một kiểu dữ liệu và đƣợc truy xuất
thông qua chỉ số.
Chỉ số bắt đầu từ 0.
Khai báo :
Mảng một chiều
<Kiểu dữ liệu> [ ] <Tên mảng> = new <Kiểu dữ liệu> [Số phần tử];
Mảng hai chiều
<Kiểu dữ liệu> [ , ] <Tên mảng> = new <Kiểu dữ liệu> [Số dòng, số
cột];
Kiểu Enum
Là một cách thức để đặt tên cho các trị nguyên, làm cho chƣơng trình rõ
ràng, dễ hiểu hơn.
Ví dụ 2.1:
enum Ngay {Hai, Ba, Tu, Nam, Sau,Bay, CN};
Hai= 0; Ba= 1; … ; CN= 6
Ví dụ 2.2:
enum Ngay {Hai=1,Ba,Tu,Nam,Sau,Bay, CN};
Hai=1; Ba=2; … ; CN=7
Kiểu struct
13
Struct dùng để nhóm các dữ liệu cùng liên quan đến một đối tƣợng nào
đó.
Khai báo :
struct <Tên cấu trúc>
{
Danh sách các thuộc tính;
}
Ví dụ 2.1: Khai báo một struct SV nhƣ sau
struct SV
{
public string ten;
public string maso;
}
* Bảng trình bày các ký tự đặc biệt
Ký tự Ý nghĩa
\' Dấu nháy đơn
\" Dấu nháy kép
\\ Dấu chéo
\0 Ký tự null
\a Alert
\b Backspace
\f Sang trang form feed
\n Dòng mới
\r Đầu dòng
\t Tab ngang
\v Tab dọc
2.2.2. Chuyển đổi kiểu dữ liệu
Ví dụ a:
short x = 10 ;
int y = x ; // chuyển đổi ngầm định
14
Ví dụ b:
short x ;
int y = 100 ;
x = (short) y; // ép kiểu tƣờng minh, trình biên dịch không báo lỗi
Ví dụ c:
short x ;
int y = 100 ;
x = y ; // không biên dịch, lỗi
2.3 Biến, hằng, toán tử
2.3.1. Biến
a) Khái niệm:
- Biến là một vùng lƣu trữ ứng với một kiểu dữ liệu.
- Biến có thể đƣợc gán giá trị và cũng có thể thay đổi giá trị trong khi thực
hiện các lệnh của chƣơng trình.
b) Khai báo biến: Sau khi khai báo biến phải gán giá trị cho biến
<Kiểu_Dữ_Liệu> <tên_biến> [ = <giá_trị> ] ;
c) Ví dụ 2.1: Khởi tạo và gán giá trị một biến
class Bien
{
static void Main()
{
// Khai bao va khoi tao bien
int bien = 9 ;
System.Console.WriteLine("Sau khi khoi tao: bien = {0}",
bien)
// Gan; gia tri cho bien
bien = 5 ;
// Xuat ra man hinh
System.Console.WriteLine("Sau khi gan: bien = {0}", bien) ;
}
}
15
2.3.2. Hằng
a) Khái niệm:
- Hằng cũng là một biến nhƣng giá trị của hằng không thay đổi trong khi thực
hiện các lệnh của chƣơng trình.
- Hằng đƣợc phân làm 3 loại:
+ Giá trị hằng (literal)
+ Biểu tƣợng hằng (symbolic constants)
+ Kiểu liệt kê (enumerations)
b) Giá trị hằng:
Ví dụ: x = 100; // 100 đƣợc gọi là giá trị hằng
c) Biểu tƣợng hằng: gán một tên hằng cho một giá trị hằng. Khai báo:
<const> <Kiểu_Dữ_Liệu> <tên_hằng> = <giá_trị> ;
Ví dụ 1.2: Nhập vào bán kính, in ra chu vi và diện tích hình tròn.
class HinhTron
{
static void Main()
{
// Khai bao bieu tuong hang
const double PI = 3.14159 ;
// Khai bao bien
xv int bankinh ;
double chuvi , dientich ;
string chuoi ;
// Nhap gia tri cho bien chuoi
System.Console.Write("Nhap ban kinh hinh tron: ") ;
chuoi = System.Console.ReadLine() ;
// Doi chuoi thanh so va gan vao bien so
bankinh = System.Convert.ToInt32(chuoi) ;
// Gan gia tri cho bien
chuvi = 2 * bankinh * PI ;
dientich = bankinh * bankinh * PI;
// Xuat ra man hinh
16
System.Console.WriteLine("Chu vi hinh tron = {0:0.00}",
chuvi) ;
System.Console.WriteLine("Dien tich hinh tron = {0:0.00}",
} dientich) ;
2.3.3.}Toán tử
2.3.3.1. Toán tử số học
+ Cộng
- Trừ
* Nhân
Ký hiệu Ý nghĩa
> Lớn hơn
>= Lớn hơn hoặc bằng
< Nhỏ hơn
<= Nhỏ hơn hoặc bằng
== Bằng
!= Khác
17
&& Và
|| Hoặc
! Phủ định
2.4 Lệnh, khối lệnh, Chú thích
Câu lệnh thực hiện một chức năng nào đó(gán, xuất, nhập, …) và kết thúc
bằng dấu chấm phẩy (;)
Khối lệnh gồm nhiều lệnh và đƣợc đặt trong cặp dấu ngoặc nhọn { }
Chú thích:
// Chú thích một dòng
/* Chú thích nhiều dòng */
2.5 Namespace
Nhƣ chúng ta đã biết .NET cung cấp một thƣ viện các lớp đồ sộ và thƣ viện này
có tên là FCL (Framework Class Library). Trong đó Console chỉ là một lớp nhỏ
trong hàng ngàn lớp trong thƣ viện. Mỗi lớp có một tên riêng, vì vậy FCL có hàng
ngàn tên nhƣ ArrayList, Dictionary, FileSelector,...
Điều này làm nảy sinh vấn đề, ngƣời lập trình không thể nào nhớ hết đƣợc tên của
các lớp trong .NET Framework. Tệ hơn nữa là sau này có thể ta tạo lại một lớp
trùng với lớp đã có chẳng hạn. Ví dụ trong quá trình phát triển một ứng dụng ta cần
xây dựng một lớp từ điển và lấy tên là Dictionary, và điều này dẫn đến sự tranh
chấp khi biên dịch vì C# chỉ cho phép một tên duy nhất.
Chắc chắn rằng khi đó chúng ta phải đổi tên của lớp từ điển mà ta vừa tạo thành
một cái tên khác chẳng hạn nhƣ myDictionary. Khi đó sẽ làm cho việc phát triển
các ứng dụng trở nên phức tạp, cồng kềnh. Đến một sự phát triển nhất định nào đó
thì chính là cơn ác mộng cho nhà phát triển.
Giải pháp để giải quyết vấn đề này là việc tạo ra một namespace, namsespace sẽ
hạn chế phạm vi của một tên, làm cho tên này chỉ có ý nghĩa trong vùng đã định
nghĩa.
Giả sử có một ngƣời nói Tùng là một kỹ sƣ, từ kỹ sƣ phải đi kèm với một lĩnh
vực nhất định nào đó, vì nếu không thì chúng ta sẽ không biết đƣợc là anh ta là kỹ
sƣ cầu đƣờng, cơ khí hay phần mềm. Khi đó một lập trình viên C# sẽ bảo rằng
Tùng là CauDuong.KySu phân biệt với CoKhi.KySu hay PhanMem.KySu.
Namespace trong trƣờng hợp này là CauDuong, CoKhi, PhanMem sẽ hạn chế phạm
18
vi của những từ theo sau. Nó tạo ra một vùng không gian để tên sau đó có nghĩa.
Tƣơng tự nhƣ vậy ta cứ tạo các namespace để phân thành các vùng cho các lớp
trùng tên không tranh chấp với nhau.
Namespace là khái niệm đƣợc sử dụng để phân nhóm các lớp đối tƣợng trong
.Net Framework, tránh cho hai lớp đối tƣợng có cùng tên.
Ví dụ:
System.Drawing2D.Pen và System.Drawing3D.Pen
đều đề cập đến một lớp đối tƣợng Pen nhƣng thuộc hai namespace khác nhau, do đó
chúng là hai lớp đối tƣợng khác nhau.
2.6 Cách tạo chƣơng trình trên C#
2.6.1 Tạo và thực thi chƣơng trình bằng ngôn ngữ C# với Console Application
Sử dụng Microsoft Visual Studio 2010
Khởi tạo một Project
19
Chọn loại ứng dụng Console Application hoặc Windows Application
Đặt tên cho dự án
Chọn nơi lƣu trữ
Bƣớc 4: Thiết kế Form – Viết code
· Thiết kế form: Nhắp vào View Designer (trong cửa số Solution
Explorer)
· Viết code: Nhắp vào View Code (trong cửa số Solution Explorer)
Bƣớc 5: Để chạy chƣơng trình, nhấn F5 hoặc nhắp vào nút
Để dừng chƣơng trình, nhấn Shift + F5 hoặc nhắp vào nút
* Các thao tác với Project / Solution
a. Tạo Project
C1. Vào menu File | New | Project
C2. Ctrl + Shift + N
C3. Chọn công cụ New Project trên thanh Standart
b. Mở Project / Solution:
C1. Vào menu File | Open | Project / Solution
C2. Ctrl + Shift + O
c. Lƣu Project / Solution
C1. Vào menu File | Save All
C2. Chọn công cụ Save All trên thanh Standart
d. Đóng Solution: Vào menu File | Close Solution
Giao diện
20
File Program.cs là file mặc định chứa hàm Main của chƣơng trình
Cấu trúc một Project:
//khai báo thƣ viện sử dụng không gian tên
using System;
namespace ConsoleApplication1
{
class Program //tên lớp, tên file = tên lớp
{
//hàm xử lý chính
static void Main(string[] args)
{
//Chƣơng trình chính viết tại đây
}
}
}
2.6.2 Tạo và thực thi chƣơng trình C# với Windows Form Application
a. Cửa sổ thiết kế Form (Designer):
21
b. Cửa sổ thiết viết code:
22
Bài tập
1. Viết chƣơng trình nhập vào 1 số nguyên n. Cho biết: a) n là số chẵn hay số lẻ ?
b) n là số âm hay số không âm ?
2. Viết chƣơng trình nhập vào 2 số thực dƣơng chỉ chiều dài và chiều rộng của
hình chữ nhật. In ra màn hình chu vi và diện tích của hình chữ nhật đó.
3. Viết chƣơng trình nhập vào một số thực dƣơng chỉ cạnh của một hình vuông.
Tính diện tích và chu vi của hình vuông đó.
4. Viết chƣơng trình nhập vào họ tên (HoTen), điểm toán (Toan), điểm lý (Ly),
điểm hoá (Hoa) của một học sinh. In ra màn hình họ tên của học sinh dƣới dạng
chữ IN HOA và điểm trung bình (Dtb) của học sinh này theo công thức: Dtb =
(Toan + Ly + Hoa) / 3
5. Viết chƣơng trình nhập bậc lƣơng (BacLuong), ngày công (NgayCong), phụ cấp
(PhuCap). Tính tiền lãnh (TienLanh) = BacLuong * 650000 * NCTL + PhuCap
Với: NCTL = NgayCong nếu NgayCong < 25
= (NgayCong – 25) * 2 + 25 nếu NgayCong >= 25
--- oOo ---
23
BÀI 3
CẤU TRÚC ĐIỀU KHIỂN – CẤU TRÚC LẶP
Mã bài: MĐ33-03
Giới thiệu:
Để có thể làm việc đƣợc với một ngôn ngữ lập trình ngoài việc tìm hiểu các
từ khoá, kiểu dữ liệu, cách khai báo biến, hằng, biểu thức…trên ngôn ngữ lập trình
thì việc hiểu và vận dụng tốt các cấu trúc điều khiển trên ngôn ngữ lập trình đó là
một vấn đề không thể thiếu khi học một ngôn ngữ lập trình. Bài học này sẽ trình
bày các cấu trúc điều khiển cơ bản trong ngôn ngữ C#
Mục tiêu:
Trình bày cú pháp của các cấu trúc điều khiển và nguyên tắc hoạt động của
những cấu trúc này trên trình biên dịch của ngôn ngữ C#;
Vận dụng đƣợc các cấu trúc điều khiển trong ngôn ngữ C# để giải các bài toán
theo yêu cầu đặt ra;
Phân tích đƣợc với bài toán nào thì sử dụng cấu trúc điều khiển nào cho phù
hợp;
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
3.1 Cấu trúc điều khiển
3.1.1. Câu lệnh if … else
a. Cú pháp:
if (Điều_Kiện)
<Khối lệnh Điều_Kiện đúng>
[else
<Khối lệnh Điều_Kiện sai>]
c. Ví dụ 3.1: Dùng câu lệnh điều kiện if … else
d.
using System;
class Chan_Le
{
static void Main()
24
{
// Khai bao va khoi tao bien
int bienDem = 9 ;
// Xuat ra man hinh
if (bienDem % 2 == 0)
Console.WriteLine("{0} la so chan", bienDem) ;
else Console.WriteLine("{0} la so le", bienDem) ;
}
3.1.2. Câu
} lệnh if lồng nhau
a. Cú pháp:
if (Điều_Kiện_1) <Khối lệnh 1>
else if (Điều_Kiện_2) <Khối lệnh 2.1>
else
<Khối lệnh 2.2>
b. Ví dụ 3.2:
using System;
class Thu_Trong_Tuan
{
static void Main()
{
// Khai bao va khoi tao bien
int thu = 5 ; // 0: Chu nhat, 1: Thu hai, 2: Thu ba, 3: Thu tu,
// 4: Thu nam, 5: Thu sau, 6: Thu bay
// Xuat ra man hinh
if ((thu == 1) || (thu == 3) || (thu == 5))
Console.WriteLine("Day la ngay 2-4-6") ;
else if ((thu == 2) || (thu == 4) || (thu == 6))
Console.WriteLine("Day la ngay 3-5-7") ;
else Console.WriteLine("Day la ngay chu nhat") ;
}
}
25
3.1.3. Câu lệnh switch
a. Cú pháp:
switch (Biểu_Thức) {
case <giá_trị_1>: < Khối lệnh 1> <Lệnh Nhảy>
case <giá_trị_2>: < Khối lệnh 2> <Lệnh Nhảy>
…. [default:
< Khối lệnh khác>] }
b. Ví dụ 3.3:
using System;
class Thu
{
static void Main()
{
// Khai bao va khoi tao bien
int thu = 5 ; // 0: Chu nhat, 1: Thu hai, 2: Thu ba, 3: Thu tu,
// 4: Thu nam, 5: Thu sau, 6: Thu bay
// Xuat ra man hinh
switch (thu)
{
case 0:
Console.WriteLine("Chu nhat") ;
break;
case 1:
Console.WriteLine("Thu hai") ;
break;
case 2:
Console.WriteLine("Thu ba") ;
break;
case 3:
Console.WriteLine("Thu tu") ;
break;
case 4:
26
Console.WriteLine("Thu nam") ;
break;
case 5:
Console.WriteLine("Thu sau") ;
break;
case 6:
Console.WriteLine("Thu bay") ;
break;
default:
Console.WriteLine("Khong phai la thu trong
tuan")
break; ;
}
}
}
3.2 Cấu trúc lặp
3.2.1. Lệnh lặp while
a. Cú pháp:
while (Điều_Kiện) < Khối lệnh>
b. Ví dụ 3.4: using
System; class
UsingWhile
{
static void Main()
{
// Khai bao va khoi tao bien dem
int i = 1 ;
// Xuat ra man hinh
while (i<=10) {
Console.WriteLine("i = {0}",i) ;
i++ ; // tang bien dem,
}
}
}
27
3.2.2. Lệnh lặp do … while
a. Cú pháp:
do < Khối lệnh> while (Điều_Kiện) ;
b. Ví dụ 3.5:
using System;
class UsingDoWhile
{
static void Main()
{
// Khai bao va khoi tao bien dem
int i = 1 ;
// Xuat ra man hinh
do {
Console.WriteLine("i = {0}",i) ;
i++ ; // tang bien dem
} while (i<= 10) ;
}
29
Bài tập
1. Viết chƣơng trình nhập vào 3 số nguyên. In ra màn hình số nguyên nhỏ nhất
trong 3 số đó.
2. Viết chƣơng trình nhập vào họ tên, điểm thi cuối kỳ của một học sinh. In ra họ
tên học sinh bằng chữ IN HOA, và kết quả xếp loại của học sinh theo tiêu
chuẩn sau:
- Giỏi: Nếu Điểm kết quả >= 8 - Khá: Nếu 8 > Điểm >= 6.5
- Trung bình: Nếu 6.5 > Điểm >= 5 - Yếu: Nếu Điểm < 5
3. Viết chƣơng trình giải phƣơng trình bậc 1: bx + c = 0
4. Viết chƣơng trình giải phƣơng trình bậc 2: ax2 + bx + c = 0
5. Viết chƣơng trình nhập vào một số nguyên cho đến khi nhận đƣợc số nguyên
dƣơng thì dừng.
6. Viết chƣơng trình nhập vào một số nguyên n. Cho biết số nguyên n có phải là số
nguyên tố không ?
7. Viết chƣơng trình nhập vào một số nguyên dƣơng n chỉ năm dƣơng lịch. Cho
biết n có phải là năm nhuận không ?
8. Viết chƣơng trình nhập vào số nguyên dƣơng n. In ra màn hình kết quả của các
tổng sau:
a) S1 = 1 + 2 + 3 + ... + n
1 1 1
b) S 2 1 ...
2 3 n
9. Viết chƣơng trình nhập vào số nguyên dƣơng n. In ra màn hình: a) Các số
nguyên dƣơng từ 1 đến n
b) Tổng và trung bình cộng của n số nguyên dƣơng này.
--- oOo ---
30
Bài 4:
MẢNG (ARRAY) – CHUỖI (STRING)- XỬ LÝ NGOẠI LỆ (EXCEPTION)
Mã bài: MĐ33-04
Giới thiệu:
Trong quá trình lập trình việc tổ chức dữ liệu cho một bài toán là vấn đề cần
đặt ra khi giải quyết bài toán đó. Đối với những bài toán đơn giản với kiểu dữ liệu
dựng sẵn có thể sử dụng để tổ chức dữ liệu thì ta sử dụng những kiểu dữ liệu dựng
sẵn trong ngôn ngữ C# để giải quyết. Nhƣng đối với những bài toán dữ liệu ở dạng
tập hợp thì kiểu mảng hoặc chuỗi là lựa chọn tối ƣu cho việc tổ chức dữ liệu cho
bài toán. Vậy mảng và chuỗi đƣợc sử dụng trong ngôn ngữ C# nhƣ thế nào?
Mục tiêu:
Trình bày đƣợc các kiến thức về mảng và danh sách mảng;
Khai báo mảng, chuỗi đúng cú pháp;
Vận dụng đƣợc dữ liệu kiểu mảng (array) và chuỗi (string) để tổ chức dữ liệu
cho bài toán;
Giải quyết đƣợc một số bài tập trên mảng, chỉ mục và tập hợp;
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
4.1. Mảng 1 chiều
4.1.1. Định nghĩa
- Mảng là một tập hợp có thứ tự của những đốitượng (objects), tất cả các đối
tƣợng này có cùng một kiểu dữ liệu.
- Mảng trong ngôn ngữ C# sử dụng những phƣơng thức và các thuộc tính. Thể
hiện của mảng trong C# có thể truy cập những phƣơng thức và các thuộc tính của
System.Array.
- Một số các thuộc tính và phƣơng thức của lớp System.Array:
Thành viên Mô tả
Sort() Phƣơng thức sắp xếp giá trị tăng dần trong mảng một chiều
Reverse() Phƣơng thức sắp xếp giá trị giảm dần trong mảng một chiều
Length Thuộc tính chiều dài của mảng
SetValue() Phƣơng thức thiết lập giá trị cho một thành phần xác định
trong mảng
31
4.1.2. Khai báo mảng:
<kiểu dữ liệu>[] <tên mảng> ;
Ví dụ:
int[] myIntArray ;
Tạo thể hiện của mảng: sử dụng từ khóa new Ví dụ:
myIntArray = new int[5] ;
4.1.3. Khởi tạo thành phần của mảng
- Tạo thể hiện của mảng đồng thời với khởi tạo các giá trị:
+ Cách 1:
int[] myIntArray = new int[5] {2, 4, 6, 8, 10};
+ Cách 2:
int[] myIntArray = {2, 4, 6, 8, 10};
- Các khai báo trên sẽ thiết lập bên trong bộ nhớ một mảng chứa 5 số nguyên.
*Chú ý: Không thể thiết lập lại kích thƣớc cho mảng.
4.1.4. Giá trị mặc định:
- Khi chúng ta tạo một mảng có kiểu dữ liệu giá trị, mỗi thành phần sẽ chứa
giá trị mặc định của kiểu dữ liệu.
Ví dụ:
Với khai báo int myIntArray = new int[5] ; thì:
- Mỗi thành phần của mảng đƣợc thiết lập giá trị là 0 (giá trị mặc định của số
nguyên).
- Những kiểu tham chiếu trong một mảng không đƣợc khởi tạo giá trị mặc
định, chúng đƣợc khởi tạo giá trị null.
4.1.5. Truy cập các thành phần trong mảng:
- Để truy cập vào thành phần trong mảng ta sử dụng toán tử chỉ mục ([]). - Chỉ
mục của thành phần đầu tiên trong mảng luôn luôn là 0.
- Thuộc tính Length của mảng cho biết số đối tƣợng trong một mảng.
4.1.6. Duyệt mảng 1 chiều:
for (int i=0; i < myIntArray.Length; i++) { xử lý
myIntArraya[i];
}
32
Có thể thay for bằng foreach nhƣ sau foreach (int phantu in
a){
xử lý myIntArraya[i]; }
Ví dụ 4.1: (Mảng 5 số nguyên từ 1 đến 5)
* Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi Du 5.1
nhƣ sau:
* Yêu cầu
- Thiết kế form nhƣ mẫu (lblKQ, btnIn, btnXoa, btnDung). - Khai báo mảng 1
chiều (a) gồm 5 số nguyên từ 1 đến 5.
- Nhắp vào button In mảng (btnIn) sẽ in ra label (lblKQ) các giá trị trong
mảng. - Nhắp vào button Xóa (btnXoa) sẽ xóa trống nội dung của label
(lblKQ).
- Nhắp vào button Dừng sẽ dừng chƣơng trình. * Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Khai báo mảng: qua code, thêm đoạn code để đƣợc kết quả nhƣ sau:
public partial class Form1 : Form
{
// Khai bao mang 1 chieu gom 5 so nguyen tu 1 den
5 int[] a = { 1, 2, 3, 4, 5 };
}
- Nhắp đúp vào button In mảng, thêm đoạn code sau:
// Xuat cac phan tu trong mang ra man hinh
this.lblKQ.Text="Các phần tử trong mảng là:\n\r"; for
(int i=0; i < a.Length; i++)
{ this.lblKQ.Text += a[i]+" "; }
33
Có thể thay for bằng foreach nhƣ sau
foreach (int phantu in a)
{ this.lblKQ.Text += a[i]+" "; }
- Nhắp đúp vào button Xóa, thêm đoạn code sau:
this.lblKQ.Text = "";
- Nhắp đúp vào button Dừng, thêm đoạn code sau:
Application.Exit();
* Yêu cầu
- Thiết kế form: btnNhap, txtNhap, btnTang, btnGiam, lblKQ, btnIn, btnXoa,
btnDung. - Khai báo mảng 1 chiều (a) chứa 5 số nguyên, số phần tử hiện có (sopt)
là 0.
- Nhập số vào TextBox txtNhap, nhắp vào button Nhập 1 phần tử mảng
(btnNhap) cho phép đƣa giá trị trong TextBox txtNhap vào mảng. Khi mảng đã đủ
5 số nguyên thì phải thông báo “Mảng đã đầy” và không cho nhập nữa.
- Nhắp vào button Sắp tăng (btnTang) sẽ sắp xếp mảng theo thứ tự tăng dần.
- Nhắp vào button Sắp giảm (btnGiam) sẽ sắp xếp mảng theo thứ tự giảm dần.
- Nhắp vào button In mảng (btnIn) sẽ in ra label (lblKQ) các giá trị trong
mảng.
- Nhắp vào button Xóa (btnXoa) sẽ xóa trống nội dung của label (lblKQ) đồng
thời khai báo lại số phần tử hiện có (sopt) của mảng là 0.
- Nhắp vào button Dừng sẽ dừng chƣơng trình.
34
* Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Khai báo mảng nhƣ sau:
public partial class Form1 : Form {
// Khai bao mang 1 chieu gom 5 so nguyen int[]
a = new int[5];
// Khai bao so phan tu hien co cua mang int
sopt = 0;
}
- Nhắp đúp vào button Nhập 1 phần tử mảng, thêm đoạn code sau:
// Nhap mot phan tu cho mang
if (sopt == 5)
MessageBox.Show("Mảng đã đầy!"); else
{
a[sopt] = Convert.ToInt32(this.txtNhap.Text);
sopt++;
this.txtNhap.ResetText(); this.txtNhap.Focus();
}
- Nhắp đúp vào button Sắp tăng, thêm đoạn code sau:
// Sap xep mang giam
if (sopt == 0)
this.lblKQ.Text = "Mảng rỗng!";
else
Array.Sort(a, 0, sopt);
this.lblKQ.Text = "Đã sắp xếp mảng tăng dần!";
- Nhắp đúp vào button Sắp giảm, thêm đoạn code sau:
// Sap xep mang giam
if (sopt == 0)
this.lblKQ.Text = "Mảng rỗng!";
else
35
Array.Reverse(a, 0, sopt);
this.lblKQ.Text = "Đã sắp xếp mảng giảm dần!";
- Nhắp đúp vào button Xóa, thêm đoạn code sau:
this.lblKQ.Text = "";
sopt = 0;
this.txtNhap.Focus();
- Nhắp đúp vào button In mảng, thêm đoạn code sau:
// Xuat cac phan tu trong mang ra man hinh
if (sopt==0)
this.lblKQ.Text = "Mảng rỗng!";
else
{
this.lblKQ.Text="Các phần tử trong mảng là:\n\r";
for (int i = 0; i < sopt; i++)
this.lblKQ.Text += a[i] + " "; }
- Nhắp đúp vào button Dừng, thêm đoạn code sau:
Application.Exit();
4.2 Mảng nhiều chiều
4.2.1. Định nghĩa
- Mảng đa chiều là mảng mà mỗi thành phần là một mảng khác. - Ngôn ngữ
C# hỗ trợ hai kiểu mảng đa chiều là:
+ Mảng đa chiều cùng kích thƣớc.
+ Mảng đa chiều khác kích thƣớc.
- Trong phạm vi bài học này, ta chỉ khảo sát mảng 2 chiều mà thôi.
4.2.2 Khai báo mảng 2 chiều <kiểu dữ liệu>[ , ] <tên mảng>
Ví dụ:
int[ , ] myRectangularArray ;
4.2.3. Khởi tạo thành phần của mảng
int[] myRectangularArray = new int[sodong , socot] ;
36
4.2.4. Duyệt mảng 2 chiều
for (int i = 0; i {< sodong; i++)
for (int j = {0; j < socot; j++)
myRectangularArray[i,j];
Ví dụ 4.3:
* Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi Du 5.3
nhƣ sau:
Yêu cầu:
- Thiết kế form nhƣ mẫu (lblKQ, btnIn, btnXoa, btnDung, btnNhap,
btnSapXep). - Khai báo mảng 2 chiều gồm 4 dòng, 3 cột chứa các số nguyên.
- Nhắp vào button Nhập mảng để nhập các phần tử cho mảng (có giá trị =
dòng - cột).
- Nhắp vào button Sắp Xếp sẽ sắp xếp mảng tăng dần theo từng hàng.
- Nhắp vào button In mảng (btnIn) sẽ in ra label (lblKQ) các giá trị trong
mảng. - Nhắp vào button Xóa (btnXoa) sẽ xóa trống nội dung của label (lblKQ).
- Nhắp vào button Dừng sẽ dừng chƣơng trình. * Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Khai báo mảng: qua code, thêm đoạn code để đƣợc kết quả nhƣ sau: public
partial class Form1 : Form
{ // Khai bao 4 dong 3 cot const int sodong = 4;
const int socot = 3;
// Khai bao mang 2 chieu gom 4 dong, 3 cot chua
12 so nguyen
int[,] Array2 = new int[sodong,socot]; }
37
- Nhắp đúp vào button Nhập mảng, thêm đoạn code sau:
// Nhap cac phan tu cho mang
for (int i=0; i < sodong; i++) for (int j=0; j<
socot; j++)
Array2[i,j]= i-j;
- Nhắp đúp vào button Sắp xếp, thêm đoạn code sau:
// Sap xep mang
int[] t = new int[sodong * socot]; for (int i = 0;
i < sodong; i++)
{
for (int j = 0; j < socot; j++)
t[j] = Array2[i, j];
Array.Sort(t, 0, socot);
for (int j = 0; j < socot; j++)
Array2[i,j]=t[j];
}
- Nhắp đúp vào button In mảng, thêm đoạn code sau:
// Xuat cac phan tu trong mang ra man hinh
this.lblKQ.Text = "Các phần tử trong mảng là:\n\r"; for
(int i = 0; i < sodong; i++)
{
for (int j = 0; j < socot; j++) {
this.lblKQ.Text += Array2[i,j] + " "; }
this.lblKQ.Text += "\n\r"; }
- Nhắp đúp vào button Xóa, thêm đoạn code sau:
this.lblKQ.Text = "";
- Nhắp đúp vào button Dừng, thêm đoạn code sau:
Application.Exit();
4.3 String (Chuỗi)
4.3.1. Tạo một chuỗi
a. Chuỗi hằng
38
string TenChuoi = "Chuỗi" ; Ví dụ:
string thongbao = "Đây là một câu thông báo." ; Chú ý: Ta có 2 khai báo
chuỗi sau là nhƣ nhau
string chuoi = "Dong mot \n Dong hai"; string chuoi = @"Dong mot
Dong hai";
b. Chuỗi dùng phƣơng thức ToString Ví dụ:
int myInt = 9 ;
string intString = myInt.ToString();
4.3.2. Thao tác trên chuỗi
Lớp string cung cấp rất nhiều các phƣơng thức để so sánh, tìm kiếm, thay thế
…; các phƣơng thức này đƣợc trình bày trong bảng sau:
Phƣơng thức Ý nghĩa
Compare() So sánh hai chuỗi (Chuỗi 1 ? Chuỗi 2) = (-1 ; 0 ; 1) tƣơng
Concat() ứng chuỗi
Nối (<, =, >)
EndsWidth() Xem chuỗi có kết thúc bằng một nhóm ký tự xác định hay
IndexOf() không.
Chỉ ra vị trí xuất hiện đầu tiên của một chuỗi con trong
Insert() chuỗi
Trả vềlớn.
một chuỗi mới đã đƣợc chèn thêm.
LastIndexOf() Chỉ ra vị trí xuất hiện cuối cùng của một chuỗi con trong
Length chuỗi lớn.
Chiều dài của chuỗi.
Remove() Xoá đi một chuỗi con.
Replace() Thay thế chuỗi cũ bằng chuỗi mới.
Split() Trả về chuỗi con đƣợc phân định bởi ký tự xác định.
StartsWidth() Xem chuỗi có bắt đầu bằng một nhóm ký tự xác định hay
Substring() không.
Lấy chuỗi con.
ToLower() Trả về bản sao của chuỗi ở kiểu chữ thƣờng.
ToUpper() Trả về bản sao của chuỗi ở kiểu chữ IN HOA.
Ví dụ 4.4
Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi_Du
4.4 nhƣ sau:
*Yêu cầu:
- Thiết kế form gồm: lblTieuDe, lbl1, txtS1, lbl2, txtS2, lbl3, lblKQ, và các
39
button (xem hình).
- Nhắp vào button Compare: so sánh 2 chuỗi txtS1 và txtS2 (có phân biệt
chữ HOA và chữ thƣờng), kết quả xuất trong lblKQ.
- Nhắp vào button COMPARE: so sánh 2 chuỗi txtS1 và txtS2 (không phân
biệt chữ HOA và chữ thƣờng), kết quả xuất trong lblKQ.
- Nhắp vào button Concat, nối 2 chuỗi txtS1 và txtS2, kết quả xuất trong
lblKQ.
- Nhắp button IndexOf, cho biết vị trí xuất hiện của chuỗi txtS2 trong chuỗi
txtS1. Nếu có txtS2 trong txtS1 thì thay thế txtS2 (trong txtS1) bằng chuỗi "CHỖ
NÀY". Xuất kết quả trong lblKQ.
- Nhắp vào button Insert, chèn chuỗi txtS2 vào sau từ đầu tiên của chuỗi
txtS1; và chèn chuỗi txtS2 vào trƣớc từ sau cùng của chuỗi txtS1. Xuất kết quả
trong lblKQ.
- Nhắp vào button Substring, cho biết vị trí xuất hiện của chuỗi "TRÌNH
XỬ LÝ" trong lblTieuDe. Nếu có thì xóa chuỗi ra khỏi lblTieuDe. Xuất kết quả
trong lblKQ.
- Nhắp button Xóa thì xóa trống: TextBox txtS1, TextBox txtS2, Label
lblKQ đồng thời đƣa con trỏ vào TextBox txtS1.
- Nhắp button Dừng thì dừng chƣơng trình.
40
* Hƣớng dẫn:
- Thiết kế form nhƣ yêu cầu.
- Nhắp đúp vào button Compare, thêm vào đoạn code: string
s1=this.txtS1.Text;
string s2=this.txtS2.Text;
// So sánh hai chuỗi với nhau có phân biệt chữ thƣờng và chữ hoa
int kq = string.Compare(s1,s2); this.lblKQ.Text =
"txtS1 ";
if (kq == -1) this.lblKQ.Text += "<";
else if (kq == 0) this.lblKQ.Text += "=";
else
this.lblKQ.Text += ">"; this.lblKQ.Text += "
txtS2";
- Nhắp đúp vào button COMPARE, thêm vào đoạn code:
String s1=this.txtS1.Text;
string s2=this.txtS2.Text;
// So sánh hai chuỗi với nhau không phân biệt chữ thƣờng và chữ
hoa
int kq = string.Compare(s1,s2, true);
this.lblKQ.Text = "txtS1 ";
if (kq == -1) this.lblKQ.Text += "<";
else if (kq == 0) this.lblKQ.Text += "=";
else
this.lblKQ.Text += ">"; this.lblKQ.Text += "
txtS2";
- Nhắp đúp vào button Concat, thêm vào đoạn code:
string s1 = this.txtS1.Text;
string s2 = this.txtS2.Text;
// Nối chuỗi
this.lblKQ.Text = string.Concat(s1,s2);
- Nhắp đúp vào button IndexOf, thêm vào đoạn code:
41
string s1 = this.txtS1.Text;
string s2 = this.txtS2.Text;
// Chỉ ra vị trí xuất hiện của chuỗi 2 trong chuỗi 1 if
(s1.IndexOf(s2) >= 0)
{
this.lblKQ.Text = "txtS2 xuất hiện trong txtS1 tại vị trí
";
this.lblKQ.Text += s1.IndexOf(s2);
this.lblKQ.Text += ".!";
// Thay thế chuỗi s="CHỖ NÀY" vào vị trí chuỗi //2 trong
chuỗi 1
string s = "CHỖ NÀY";
this.lblKQ.Text += "\n\rThay thế txtS2 trong txtS1
bằng chuỗi CHỖ NÀY,";
this.lblKQ.Text+="\n\rKết quả:"+s1.Replace(s2,
s);
}
else this.lblKQ.Text = "txtS2 không xuất hiện trong
txtS1!";
- Nhắp đúp vào button Insert, thêm vào đoạn code:
string s1 = this.txtS1.Text;
string s2 = this.txtS2.Text;
// Chèn chuỗi 2 vào sau từ đầu tiên của chuỗi 1
this.lblKQ.Text = "Chèn txtS2 vào sau từ đầu tiên của
txtS1:\n\r";
this.lblKQ.Text += s1.Insert(s1.IndexOf(" "), s2);
// Chèn chuỗi 2 vào trƣớc từ cuối cùng của chuỗi 1
this.lblKQ.Text += "\n\rChèn txtS2 vào trƣớc từ cuối cùng của
txtS1:\n\r ";
this.lblKQ.Text += s1.Insert(s1.LastIndexOf("
"),s2);
- Nhắp đúp vào button Substring, thêm vào đoạn code:
42
// Chỉ ra vị trí xuất hiện của s="TRÌNH XỬ LÝ"
// trong lblTieuDe
string s="TRÌNH XỬ LÝ";
int ix;
ix = this.lblTieuDe.Text.IndexOf(s);
// Trích s từ lblTieuDe và in vào lblKQ
if (ix >= 0)
{
this.lblKQ.Text =
this.lblTieuDe.Text.Substring(ix, s.Length);
this.lblKQ.Text += " xuất hiện trong tiêu đề tại vị trí ";
this.lblKQ.Text += ix;
// Xóa s ra khỏi lblTieuDe
this.lblKQ.Text += "\n\rTiêu đề sau khi xóa " + s;
this.lblKQ.Text += "\n\rKết quả là: ";
this.lblKQ.Text +=
this.lblTieuDe.Text.Remove(ix, s.Length);
}
else this.lblKQ.Text = "Không xuất hiện trong chuỗi 1!";
- Nhắp đúp vào button Xóa, thêm vào đoạn code:
this.txtS1.ResetText(); this.txtS2.ResetText();
this.lblKQ.Text = ""; this.txtS1.Focus();
- Nhắp đúp vào button Dừng, thêm vào đoạn code:
Application.Exit();
Ví dụ 4.5
Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi_Du_4.5
nhƣ sau:
43
* Yêu cầu
- Thiết kế form gồm: lbl1, txtHoTen, lbl2, lblKQ, và các button (xem hình).
- Nhắp vào button CountOfWord: đếm số từ trong chuỗi txtHoTen, kết quả
xuất trong lblKQ.
- Nhắp vào button Proper: đổi thành chữ Hoa Đầu Từ cho chuỗi txtHoTen, kết
quả xuất trong lblKQ.
- Nhắp vào button Left, lấy ra từ bên trái của chuỗi txtHoTen, kết quả xuất
trong lblKQ.
- Nhắp vào button Right, lấy ra từ bên phải của chuỗi txtHoTen, kết quả xuất
trong lblKQ.
- Nhắp button Xóa thì xóa trống: TextBox txtS1, TextBox txtS2, Label lblKQ
đồng thời đƣa con trỏ vào TextBox txtS1.
- Nhắp button Dừng thì dừng chƣơng trình.
* Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Nhắp đúp vào button CountOfWord, thêm vào đoạn code:
// Đếm số từ trong chuỗi
// Tạo ra hằng ký tự khoảng trắng
const char Space =' ';
// Gán giá trị cho chuỗi
string s = hoten.Trim();
// Thực hiện việc chia chuỗi vào mảng --> Đếm từ = độ dài mảng
44
int cWord = s.Split(Space).Length;)
// Gởi kết quả trả về
return cWord;
** Tham khảothêmđoạn code sau:
// Đếm số từ trong chuỗi txtHoTen
int count=0;
string s=this.txtHoTen.Text.Trim();
for (int i = 0; i < s.Length-1; i++)
if ((s.Substring(i, 1)==" ") && (s.Substring(i
+ 1, 1)!=" "))
count++;
if (s.Length > 0) count++;
this.lblKQ.Text = "Số từ là: " + count;
- Nhắp đúp vào button Proper, thêm vào đoạn code:
// Đổi thành chữ Hoa Đầu Từ trong chuỗi txtHoTen string s =
this.txtHoTen.Text.Trim();
if (s.Length == 0)
this.lblKQ.Text = "Chuỗi rỗng!";
else
{
this.lblKQ.Text = "Chuỗi kết quả là: ";
this.lblKQ.Text += s.Substring(0,1).ToUpper();
for (int i = 1; i < s.Length; i++)
{
if ((s[i-1].ToString()==" "
)&&(s[i].ToString()!=" "))
{
string ss = s[i].ToString();
this.lblKQ.Text += ss.ToUpper();
}
else this.lblKQ.Text += s[i].ToString();
45
}
}
- Nhắp đúp vào button Left, thêm vào đoạn code:
// Từ đầu tiên của chuỗi txtHoTen
string s = this.txtHoTen.Text.Trim();
if (s.Length == 0)
this.lblKQ.Text = "Chuỗi rỗng!";
else
{
this.lblKQ.Text = "Từ đầu tiên của chuỗi là: ";
this.lblKQ.Text += s.Substring(0, s.IndexOf("
"));
}
- Nhắp đúp vào button Right, thêm vào đoạn code:
// Từ cuối cùng của chuỗi txtHoTen
string s = this.txtHoTen.Text.Trim();
if (s.Length == 0)
this.lblKQ.Text = "Chuỗi rỗng!";
else
{
this.lblKQ.Text = " Từ đầu tiên của chuỗi là:";
this.lblKQ.Text +=
s.Substring(s.LastIndexOf(" ")+1, s.Length-
s.LastIndexOf(" ") - 1);
}
- Nhắp đúp vào button Xóa, thêm vào đoạn code:
this.txtHoTen.ResetText();
this.lblKQ.Text = "";
this.txtHoTen.Focus();
- Nhắp đúp vào button Dừng, thêm vào đoạn code:
Application.Exit();
46
4.4 Exception (Ngoại lệ)
4.4.1. Khái niệm
- Exception có thể đƣợc hiểu là bắt giữ lỗi với những đoạn mã hợp lệ để
không tổn hại đến chƣơng trình.
- Lỗi có thể do nguyên nhân từ chính ngƣời sử dụng; hoặc có thể do những
vấn đề không mong đợi khác nhƣ: thiếu bộ nhớ, thiếu tài nguyên hệ thống ….
- Một trình xử lý ngoại lệ là một khối lệnh chƣơng trình đƣợc thiết kế xử lý
các ngoại lệ mà chƣơng trình phát sinh.
- Xử lý ngoại lệ đƣợc thực thi trong trong câu lệnh catch.
- Các câu lệnh có khả năng xảy ra ngoại lệ thực thi trong câu lệnh try.
* Một cách lý tƣởng, nếu một ngoại lệ đƣợc bắt và đƣợc xử lý thì chƣơng
trình có thể sửa chữa đƣợc vấn đề bị lỗi và tiếp tục thực hiện hoạt động. Thậm
chí nếu chƣơng trình không tiếp tục, bằng việc bắt giữ ngoại lệ chúng ta cũng có
cơ hội để in ra những thông điệp có ý nghĩa và kết thúc chƣơng trình một cách
rõ ràng.
4.4.2. Ví dụ
Ví dụ 4.6
Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi Du
4.6 nhƣ sau:
* Yêu cầu:
- Thiết kế form gồm: lbl1, txtSo1, lbl2, txtSo2, lbl3, txtKQ và các button (xem
hình). - Nhập số vào 2 TextBox txtSo1 và TxtSo2.
- Nhắp vào button Chia:
+ Nếu txtSo1, txtSo2 không phải là số; hoặc nhập vào txtSo2 là 0 thì báo lỗi:
“Lỗi rồi!”. + Nếu txtSo1, txtSo2 là số thì xuất kết quả là txtSo1 / txtSo2 vào
TextBox txtKQ.
- Nhắp button Xóa thì xóa trống: TextBox txtS1, TextBox txtS2, Label lblKQ
47
đồng thời đƣa con trỏ vào TextBox txtS1.
- Nhắp buuton Dừng thì dừng chƣơng trình. * Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Nhắp đúp vào button Chia, thêm vào đoạn code:
// Xóa trống TextBox txtKQ txtKQ.ResetText();
// Đoạn code có xảy ra ngoại lệ khi thực hiện
try
{
int so1 = int.Parse(this.txtSo1.Text); int so2
= int.Parse(this.txtSo2.Text); this.txtKQ.Text
+= (float)so1 / so2;
}
// Xử lý ngoại lệ
catch (Exception ex)
{
this.txtKQ.Text = "Lỗi rồi!";
}
48
Bài tập
Khởi tạo một ứng dụng Windows Forms Application:
1. Khai báo 1 mảng nguyên 1 chiều tối đa 10 phần tử. Viết chƣơng trình:
- Nhập vào giá trị cho 1 phần tử trong mảng.
- In giá trị của các phần tử trong mảng.
- In giá trị lớn nhất, giá trị nhỏ nhất của các phần tử trong mảng.
- In tổng số các giá trị, trung bình cộng các giá trị của các phần tử trong mảng.
2. Khai báo 1 mảng nguyên 2 chiều 4 dòng, 5 cột. Viết chƣơng trình:
- Nhập giá trị cho các phần tử trong mảng (giá trị = số thứ tự dòng + số thứ tự
cột). - In giá trị các phần tử trong mảng.
- In giá trị lớn nhất, giá trị nhỏ nhất của các phần tử trong mảng.
- In tổng số các giá trị, trung bình cộng các giá trị của các phần tử trong mảng.
3. Viết chƣơng trình nhập vào một chuỗi họ và tên. In ra: - Độ dài và số từ của
chuỗi họ tên.
- Chuỗi họ và tên dƣới dạng chữ thƣờng.
- Chuỗi họ và tên dƣới dạng chữ IN HOA. - Chuỗi họ và tên dƣới dạng Hoa
Đầu Từ.
- Chuỗi họ và tên đã đƣợc loại bỏ các khoảng trắng thừa (đầu chuỗi, cuối chuỗi,
bên trong chuỗi).
4. Viết chƣơng trình giải phƣơng trình bậc 1: bx + c = 0
Lƣu ý: có xử lý trƣờng hợp nhập vào b, c không phải là số.
-- oOo --
49
Bài 5
LỚP (CLASS) – ĐỐI TƢỢNG (OBJECT)-
PHƢƠNG THỨC (METHOD)
Mã bài: MĐ33-05
Giới thiệu:
Trong phƣơng pháp lập trình thủ tục, chƣơng trình là một hệ thống các thủ tục,
hàm. Tức là, khi viết chƣơng trình, ta phải xác định chƣơng trình làm những công
việc (thao tác) nào? Mỗi thao tác gồm những thao tác con nào? Từ đó mỗi thao tác sẽ
tƣơng ứng với một hàm. Nhƣ vậy, lập trình theo phƣơng pháp thủ tục là xác định các
hàm, định nghĩa các hàm và gọi các hàm này để giải quyết vấn đề đƣợc đặt ra.
Một trong những nhƣợc điểm của phƣơng pháp này là mọi hàm đều có thể truy
cập biến toàn cục hoặc dữ liệu có thể phải truyền qua rất nhiều hàm trƣớc khi đến
đƣợc hàm thực sự sử dụng hoặc thao tác trên nó. Điều này dẫn đến sự khó kiểm soát
khi chƣơng trình quá lớn và khi phát triển, sửa đổi chƣơng trình.
Một khó khăn nữa đó là việc nhớ các hàm xây dựng sẵn khi số lƣợng hàm quá
nhiều.
Phƣơng pháp lập trình hƣớng đối tƣợng sẽ khắc phục đƣợc những nhƣợc điểm
của phƣơng pháp lập trình thủ tục. Phƣơng pháp lập trình này lấy đối tƣợng làm nền
tảng để xây dựng chƣơng trình. Đối tƣợng là sự gắn kết giữa dữ liệu của đối tƣợng và
các hàm (còn gọi là phƣơng thức) thao tác trên các dữ liệu này.
Đối tƣợng = Dữ liệu + Phƣơng thức
Khi viết chƣơng trình theo phƣơng pháp hƣớng đối tƣợng ta phải trả lời các câu
hỏi:
Chƣơng trình liên quan tới những lớp đối tƣợng nào?
Mỗi đối tƣợng cần có những dữ liệu và thao tác nào?
Các đối tƣợng quan hệ với nhau nhƣ thế nào trong chƣơng trình?
Từ đó ta thiết kế các lớp đối tƣợng và tổ chức trao đổi thông tin giữa các đối
tƣợng, ra lệnh để đối tƣợng thực hiện các nhiệm vụ thích hợp.
Mục tiêu:
Thực hiện thuần thục kỹ năng tạo lớp, tạo đối tƣợng;
Vận dụng đƣợc kiến thức và kỹ năng về các phƣơng thức, các thành phần static;
Vận dụng đƣợc kiến thức và kỹ năng về tham số và các phƣơng thức nạp chồng;
50
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
5.1 Lớp và đối tƣợng
5.1.1. Khái niệm
- Kiểu dữ liệu trong C# đƣợc định nghĩa là một lớp (class).
- Thể hiện riêng của từng lớp đƣợc gọi là đối tƣợng (object).
- Hai thành phần chính cấu thành một lớp (class) là thuộc tính / tính chất và
phƣơng thức (method) / hành động ứng xử của đối tƣợng.
Lớp đối tƣợng:
Định nghĩa các thuộc tính (đặc điểm) và hành động (phƣơng thức) chung cho
tất cả các đối tƣợng của cùng một loại.
Đối tƣợng:
Thể hiện cụ thể của một lớp đối tƣợng.
Ví dụ:
Lớp SINHVIEN có
- Thuộc tính: Họ tên, giới tính, ngày tháng năm sinh, điểm trung bình, đối
tƣợng ƣu tiên, ...
- Phƣơng thức: Học bài, làm bài thi, làm bài tập, ...
- Sinh viên Nguyễn Văn A, Lý Thị B là đối tƣợng thuộc lớp SINHVIEN
5.1.2 Tạo và sử dụng đối tƣợng lớp (class)
5.1.2.1. Định nghĩa lớp
class <Tên lớp>
{
<từ khóa truy xuất> thuộc tính;
<từ khóa truy xuất> phƣơng thức();
}
Các từ khoá truy xuất: Chỉ phạm vi hoạt động
private (mặc định): Chỉ đƣợc truy xuất trong nội bộ lớp (thuộc tính
thƣờng sử dụng).
protected: Truy xuất trong nội bộ lớp hoặc trong các lớp con, đƣợc sử
dụng cho lớp cơ sở (lớp cha)
51
public: Truy xuất mọi nơi(phƣơng thức thƣờng sử dụng).
static: Truy xuất không cần khởi tạo đối tƣợng của lớp.
52
hsA.Xuat();
Ví dụ 5.4:
Chƣơng trình nhập chiều dài, chiều rộng của hình chữ nhật và xuất ra
diện tích, chu vi của hình chữ nhật.
using System; namespace LopDoiTuongHCN {
classHCN {
protected float Dai, Rong; public float ChuVi()
{
return (Dai + Rong )*2;
}
public float DienTich()
{
return Dai* Rong;
}
public void Nhap()
{
Console.WriteLine("Nhap chieu dai: ");
Dai = float.Parse(Console.ReadLine()); Console.WriteLine("Nhap chieu rong:
");
Rong = float.Parse(Console.ReadLine());
}
public void Xuat()
{
Console.WriteLine("Hinh chu nhat: Dai = {0}, Rong = {1}", Dai, Rong);
}
}
class Application {
static void Main(string[] args)
{ HCN h;
h = new HCN(); h.Nhap () ; h.Xuat();
53
Console.WriteLine ("Chu vi hinh chu nhat: {0}", h.ChuVi());
Console.WriteLine ("Dien tich hinh chu nhat: {0}", h.DienTich());
Console.ReadLine();
}
}
Ví dụ 5.5:
* Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là
Vi_Du_5.5 nhƣ sau:
* Yêu cầu
- Thiết kế form gồm: lbl1, txtHoTen, lbl2, lblKQ, và các button (xem hình). -
Tạo class Chuoi nhƣ sau:
public class Chuoi {
// Thuộc tính ...
// Phƣơng thức ...
}
57
button Hoa Đầu Từ, thêm vào đoạn code:
Chuoi s = new Chuoi(); this.lblKQ.Text = "Kết
quả là: " ; this.lblKQ.Text += s.Proper(this.txtHoTen.Text);
- Nhắp đúp vào button Xóa, thêm vào đoạn code: this.lblKQ.Text = "";
this.txtHoTen.ResetText(); this.txtHoTen.Focus();
- Nhắp đúp vào button Dừng, thêm vào đoạn code: Application.Exit();
* Bổ sung
- Nút button In Hoa: đổi thành chuỗi IN HOA của chuỗi trong txtHoTen .
- Nút button In Thƣờng: đổi thành chuỗi in thƣờng của chuỗi trong txtHoTen.
5.2 Properties - Method
5.2.1. Thuộc tính (Properties):
Thuộc tính là những thông tin có thể thay đổi đƣợc.
5.2.2 Thuộc tính truy cập
Thuộc tính Phạm vi truy cập
public Không hạn chế. Những thành viên đƣợc đánh dấu public
có thể đƣợc dùng bất kỳ các phƣơng thức của lớp, bao
gồm cả những lớp khác.
private Thành viên trong lớp đƣợc đánh dấu private chỉ đƣợc
dùng các phƣơng thức của lớp này mà thôi.
Protected Thành viên trong lớp đƣợc đánh dấu protected chỉ đƣợc
dùng các phƣơng thức của lớp này; và các phƣơng thức
của lớp dẫn xuất từ lớp này.
Internal Thành viên trong lớp đƣợc đánh dấu là internal đƣợc
dùng các phƣơng thức của bất kỳ lớp nào cùng khối hợp
ngữ với lớp này.
protected Thành viên trong lớp đƣợc đánh dấu là protected internal
internal đƣợc dùng các phƣơng thức của lớp này; các phƣơng
thức của lớp dẫn xuất từ lớp này; và các phƣơng thức của
bất kỳ lớp nào trong cùng khối hợp ngữ với lớp này.
58
5.2.4. Tham số của phƣơng thức
a) Khái niệm:
- Các tham số theo sau tên phƣơng thức và đƣợc bọc bên trong dấu ngoặc tròn
(). - Mỗi tham số phải khai báo kèm theo kiểu dữ liệu.
- Trong C# có 2 dạng truyền tham số:
+ Truyền tham chiếu: dùng thêm từ khóa ref. + Truyền tham trị
b) Ví dụ:
* Truyền tham số cho phƣơng thức theo kiểu tham chiếu
public class Hoandoi
{
public void HoanVi(ref int a, ref int b)
{
int c = a ; a = b ;
b=c;
}
}
Khi đó: khi gọi hàm HoanVi ta phải truyền tham số dƣới dạng tham chiếu nhƣ
sau: HoanDoi s = new HoanDoi();
s.HoanVi(ref a, ref b);
* Truyền tham số cho phƣơng thức theo kiểu tham trị
public class HoanDoi
{
public void HoanVi(int a, int b)
{
int c = a ; a = b ;
b=c;
}
}
Ví dụ 5.6
* Khởi tạo một ứng dụng Windows Forms Application, lƣu với tên là Vi_Du_5.2
59
nhƣ sau:
* Yêu cầu
- Thiết kế form gồm: lbl1, txta, lbl2, txtb, và các button (xem hình).
- Tạo các class HoanDoi, trong class có hàm HoanVi cho phép hoán vị 2 giá trị số
nguyên.
- Nhắp button Hoán Đổi sẽ hoán đổi 2 giá trị trong txta và txtb
- Nhắp button Xóa sẽ xóa trống 2 TextBox và đƣa con trỏ vào ô txta. - Nhắp
button Dừng sẽ dừng chƣơng trình.
* Hƣớng dẫn
- Thiết kế form nhƣ yêu cầu.
- Khai báo class: qua code, thêm đoạn code để đƣợc kết quả nhƣ sau
public Form1()
{
InitializeComponent();
}
public class HoanDoi
{
public void HoanVi(ref int a,ref int b)
{
int c = a ; a = b ;
b=c;
}
}
- Nhắp đúp vào button Hoán đổi, thêm vào đoạn code:
int a = int.Parse(this.txta.Text); int b = int.Parse(this.txtb.Text);
60
HoanDoi s = new HoanDoi();
s.HoanVi(ref a, ref b);
this.txta.Text = a.ToString();
this.txtb.Text = b.ToString();
- Nhắp đúp vào button Xóa, thêm vào đoạn code: this.txta.ResetText();
this.txtb.ResetText(); this.txta.Focus();
- Nhắp đúp vào button Dừng, thêm vào đoạn code: Application.Exit();
c) Tham chiếu this
Khi một đối tƣợng thực thi một phƣơng thức của thể hiện (không phải là
phƣơng thức tĩnh) tham chiếu this tự động trỏ đến đối tƣợng này. Mọi phƣơng thức
của đối tƣợng đều có thể tham chiếu đến các thành phần của đối tƣợng thông qua
tham chiếu this. Có 3 trƣờng hợp sử dụng tham chiếu this:
- Tránh xung đột tên khi tham số của phƣơng thức trùng tên với tên biến dữ liệu
của đối tƣợng.
- Dùng để truyền đối tƣợng hiện tại làm tham số cho một phƣơng thức khác
(chẳng hạn gọi đệ quy)
- Dùng với mục đích chỉ mục.
Ví dụ 5.7
Trƣờng hợp tên tham số trùng với tên thuộc tính của đối tƣợng ta dùng từ khóa
this. Từ khoá this đƣợc dùng để tham chiếu đến chính bản thân của đối tƣợng đó
class ViDu
{
int a, b;
public void GanGiaTri(int a, int b)
{
this.a = a;
this.b = b;
}
public void Xuat()
{
Console.WriteLine("a={0}, b={1}", a, b);
61
}
}
5.3 Phƣơng thức tạo lập (constructor) của một đối tƣợng
Phƣơng thức tạo lập của một đối tƣợng có các tính chất sau:
■ Đƣợc gọi đến một cách tự động khi một đối tƣợng của lớp đƣợc tạo ra. Dùng để
khởi động các giá trị đầu cho các thành phần dữ liệu của đối tƣợng thuộc lớp.
■ Tên phƣơng thức giống với tên lớp và có mức độ truy cập là public.
■ Không có giá trị trả về.
■ Trƣớc khi phƣơng thức tạo lập chạy, đối tƣợng chƣa thực sự 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 lớp.
■ Khi ta không định nghĩa một phƣơng thức tạo lập nào cho lớp, trình biên dịch sẽ
tự động tạo một phƣơng thức tạo lập mặc định cho lớp đó và khởi tạo các biến
bằng các giá trị mặc định.
Thông thƣờng ta nên định nghĩa một phƣơng thức tạo lập cho 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 lớp.
Chú ý rằng, nếu lớp có phƣơng thức tạo lập có tham số thì khi khởi tạo đối tƣợng
(bằng toán tử new) ta phải truyền tham số cho phƣơng thức tạo lập theo cú pháp:
TênBiếnĐốiTƣợng = new TênLớp(DanhSáchĐốiSố);
Ví du 5.8:
Xây dựng một lớp Time trong đó có một phƣơng thức tạo lập nhận tham số có
kiểu DateTime (kiểu xây dựng sẵn của trình biên dịch) làm tham số khởi gán cho các
thành phần dữ liệu của đối tƣợng thuộc lớp Time.
using System;
public class Time
{
// Các biến thành viên
int Year;
int Month;
int Date;
int Hour;
int Minute;
62
int Second = 30;
public void DisplayCurrentTime( )
{
Console.WriteLine("Current time is: {0}/{1}/{2} {3}:{4}:{5}",Month, Date,
Year, Hour, Minute, Second);
}
public Time(System.DateTime dt)// Phương thức khởi tạo
{
Console.WriteLine("Ham constructor tu dong duoc goi!");
Year = dt.Year;
Month = dt.Month;
Date = dt.Day;
Hour = dt.Hour;
Minute = dt.Minute;
}
class DateTimeConstrcutorApp
{
static void Main( )
{
System.DateTime System.DateTime.Now;
Time t = new Time(currentTime); t.DisplayCurrentTime( );
Console.ReadLine();
}
}
5.4 Phƣơng thức tạo lập sao chép (copy constructor)
Phƣơng thức tạo lập sao chép khởi gán giá trị cho đối tƣợng mới bằng cách sao chép
dữ liệu của đối tƣợng đã tồn tại (cùng kiểu). Ví dụ, ta muốn truyền một đối tƣợng
Time t1 để khởi gán cho đối tƣợng Time t2 mới với mục đích làm cho t2 có giá trị
giống t1, ta sẽ xây dựng phƣơng thức tạo lập sao chép của lớp Time nhƣ sau:
public Time(Time existingTimeObject)
63
{
Year = existingTimeObject.Year;
Month =
Date =existingTimeObject.Month;
existingTimeObject.Date;
Hour = existingTimeObject.Hour;
Minute =
Second existingTimeObject.Minute;
=
existingTimeObject.Second;
}
Khi đó cú pháp khai báo t2 là:
Time t2 = new Time(t1).
Khi đó hàm copy constructor đƣợc gọi và gán giá trị của t1 cho t2.
5.5 Sử dụng các thành viên tĩnh (static member)
Dữ liệu và phƣơng thức của một lớp có thể là thành viên thuộc thể hiện của lớp
(đối tƣợng) hoặc thành viên tĩnh (có từ khóa static đứng trƣớc). Thành viên thể hiện
đƣợc kết hợp riêng với từng đối tƣợng của lớp. Nhƣ vậy, trong cùng một lớp, các đối
tƣợng khác nhau có những biến dữ liệu cùng tên, cùng kiểu nhƣng đƣợc cấp phát ở
các vùng nhớ khác nhau và giá trị của chúng cũng có thể khác nhau. Trong khi đó,
thành viên tĩnh (biến, phƣơng thức) đƣợc coi là phần chung của các đối tƣợng trong
cùng một lớp. Mọi đối tƣợng thuộc lớp đều có thể truy cập thành viên tĩnh. Nói cách
khác, các thành viên thể hiện đƣợc xem là toàn cục trong phạm vi từng đối tƣợng còn
thành viên tĩnh đƣợc xem là toàn cục trong phạm vi một lớp.
Việc truy cập đến thành viên tĩnh phải thực hiện thông qua tên lớp
Những thuộc tính và phƣơng thức trong một lớp có thể là những thành viên thể hiện
(instance members) hay những thành viên tĩnh (static members). Những thành viên
thể hiện hay thành viên của đối tƣợng liên quan đến thể hiện của một kiểu dữ liệu.
Trong khi thành viên tĩnh đƣợc xem nhƣ một phần của lớp. Chúng ta có thể truy cập
đến thành viên tĩnh của một lớp thông qua tên lớp đã đƣợc khai báo (không đƣợc truy
cập thành viên tĩnh thông qua đối tƣợng) theo cú pháp:
TênLớp.TênThànhViênTĩnh
Chú ý:
• Phương thức tĩnh thao tác trên các dữ liệu tĩnh và khống thể truy cập trực
tiếp các thành viên khống tĩnh.
64
Ngoài ra, ta có thể định nghĩa một phƣơng thức tạo lập tĩnh, phƣơng thức này
dùng để khởi gán giá trị cho biến tĩnh của lớp và sẽ chạy trƣớc khi thể hiện của đầu
tiên lớp đƣợc tạo. Phƣơng thức tạo lập tĩnh hữu dụng khi chúng ta cần cài đặt một số
công việc mà không thể thực hiện đƣợc thông qua chức năng khởi dựng và công việc
cài đặt này chỉ đƣợc thực hiện duy nhất một lần.
Ví dụ 5.9:
Biến thành viên tĩnh đƣợc dùng với mục đích theo dõi số thể hiện hiện tại của lớp.
using System; public class Cat {
private static int SoMeo = - 6; // bien tinh private string TenMeo ;
// Phuong thuc tao lap cua doi tuong public Cat( string T)
{
TenMeo = T ;
Console.WriteLine("WOAWMM {0} day!", TenMeo); SoMeo++;
}
// Phuong thuc tao lap tinh static Cat( )
{
Console.WriteLine("Bat dau lam thit meo !!!!"); SoMeo = 0;
}
public static void HowManyCats( )
{
Console.WriteLine("Dang lam thit {0} con meo!",SoMeo);
}
}
public class Tester
{
static void Main( )
{
Cat.HowManyCats( );
Cat tom = new Cat("Meo Tom" );
Cat.HowManyCats( );
65
Cat muop = new Cat("Meo Muop");
Cat.HowManyCats( );
//Tom.HowManyCats( ); ----- > Error
Console.ReadLine();
}
}
Trong ví dụ này, ta xây dựng lớp Cat với một biến tĩnh SoMeo để đếm số thể
hiện (số mèo) hiện có và một biến thể hiện TenMeo để lƣu tên của từng đối tƣợng
mèo. Nhƣ vậy, mỗi đối tƣơng tom, muop đều có riêng biến TenMeo và chúng dùng
chung biến SoMeo.
Ban đầu biến SoMeo đƣợc khởi gán giá trị -6, nhƣng khi đối tƣợng tom (đối tƣợng
đầu tiên của lớp Cat) đƣợc tạo ra, phƣơng thức tạo lập tĩnh static Cat() tự động thực
hiện và gán lại giá trị 0 cho biến tĩnh này.
Mỗi khi một đối tƣợng thuộc lớp Cat đƣợc tạo ra thì phƣơng thức tạo lập của
đối tƣợng này truy cập đến biến đếm SoMeo và tăng giá trị của biến này lên một đơn
vị. Nhƣ vậy, khi đối tƣợng tom đƣợc tạo ra, giá trị của biến này tăng lên thành 1, khi
đối tƣợng muop đƣợc tạo ra, giá trị của biến này tăng lên thành 2. Phƣơng thức tĩnh
HowManyCats() thực hiện nhiệm vụ xuất biến tĩnh SoMeo thông qua tên lớp bằng
câu lệnh:
Cat.HowManyCats( );
Nếu ta gọi lệnh sau thì trình biên dịch sẽ báo lỗi:
tom.HowManyCats( );
- Ghi chú:
Trong ngôn ngữ C# không cho phép truy cập đến các phƣơng thức tĩnh và các
biến thành viên tĩnh thông qua một thể hiện, nếu chúng ta cố làm điều đó thì trình
biên dịch C# sẽ báo lỗi, điều này khác với ngôn ngữ C++.
5.6 Đóng gói dữ liệu với thuộc tính (property)
Thuộc tính là một đặc tính mới đƣợc giới thiệu trong ngôn ngữ C# làm tăng sức
mạnh của tính đóng gói. Thuộc tính đơn giản là các phƣơng thức lấy giá trị (get) và
gán giá trị (set). Nó cho phép truy cập đến các thành phần dữ liệu của đối tƣợng ở
mức độ đọc hoặc ghi hoặc cả hai và che dấu cài đặt thực sự bên trong lớp. Một thuộc
tính thƣờng quản lý một biến dữ liệu của lớp và thuộc tính đó có thể là:
Chỉ đọc (rread-only): chỉ có phƣơng thức get. Ta chỉ đƣợc đọc giá trị của thuộc
tính.
66
Chỉ ghi (write-only): chỉ có phƣơng thức set. Ta chỉ đƣợc gán (ghi dữ liệu) giá
trị cho thuộc tính.
Vừa đọc vừa ghi (read/write): có cả hai phƣơng thức get và set. Đƣợc phép đọc
và ghi giá trị.
Để khai báo một thuộc tính, chúng ta viết kiểu thuộc tính và tên theo sau bởi cặp {}.
Bên trong cặp {} chúng ta có thể khai báo các phƣơng thức get hay set.
Cú pháp định nghĩa một thuộc tính:
public KiểuTrảVề TênThuộcTính
{
// phƣơng thức lấy giá trị của thuộc tính
get
{
//...các câu lệnh
return BiểuThứcTrảVề; //trả về một giá trị
}
//phƣơng thức gán giá trị cho thuộc tính set
{
//...các câu lệnh BiếnThànhViên = value;
}
}
Phần thân của phƣơng thức get của thuộc tính tƣơng tự nhƣ phần thân phƣơng
thức của lớp. Chúng trả về giá trị (hoặc tham chiếu) nào đó, thƣờng là trả về giá trị
của biến thành viên mà thuộc tính quản lý. Khi ta truy cập đến thuộc tính thì phƣơng
thức get đƣợc gọi thực hiện.
Phƣơng thức set của thuộc tính dùng để gán giá trị cho biến thành viên mà thuộc tính
quản lý. Khi định nghĩa phƣơng thức set, ta phải sử dụng từ khóa value để biểu diễn
cho giá trị dùng để gán cho biến thành viên. Khi gán một giá trị cho thuộc tính thì
phƣơng thức set tự động đƣợc gọi và tham số ẩn value chính là giá trị dùng để gán.
Ví dụ 5.10:
class ViDu
{
private int a, b;
67
public void Nhap()
{}
public void Xuat()
{}
public int A
{
get{return a;}
set{a=value;}
}
public int B
{
get {return b;}
set { b = value; }
}
}
class Program
{
static void Main(string[] args)
{
ViDu vd = new ViDu();
vd.A = 5 + vd.B;
vd.Xuat();
}
}
Ví dụ 5.11: Xây dựng ứng dụng có đóng gói dữ liệu với thuộc tính Property
using System;
class Student
{
string _Ten ;
68
float _DiemToan, _DiemTin;
float _DiemTB;
}
// Phƣơng thức khởi tạo
public Student()
{
_Ten = "";
_DiemToan = 0;
_DiemTin = 0;
_DiemTB = 0;
}
// thuoc tinh ten - (read/write)
public string Ten
{
get {return _Ten;}
set {_Ten = value;}
}
//Thuoc tinh diem toan - (read/write)
public float DiemToan
{
get {return _DiemToan;}
set {
_DiemToan = value;
_DiemTB = (_DiemToan + DiemTin)/2;
}
}
//Thuoc tinh diem tin - (read/write)
public float DiemTin
{
69
get {return _DiemTin;}
set {
_DiemTin = value;
_DiemTB = (_DiemToan + DiemTin)/2;
}
}
//Thuoc tinh diem tin - (read only)
public float DiemTrungBinh
{
get {return _DiemTB;}
}
}
class Student_PropertyApp
{
static void Main(string[] args)
{
Student si = new Student();
si.Ten = "Hoa"; si.DiemToan = 5; si.DiemTin = 7;
//si.DiemTrungBinh = 6; -------- > loi
Console.WriteLine("Ten: {0}, diem Toan: {i}, diem Tin: {2}, diem trung
binh: {3}", si.Ten, si.DiemToan, si.DiemTin, si.DiemTrungBinh);
Console.ReadLine();
}
}
5.7 Toán tử
Trong C#, toán tử là một phƣơng thức tĩnh dùng để quá tải một phép toán nào
đó trên các đối tƣợng. Mục đích của toán tử là để viết mã chƣơng trình gọn gang, dễ
hiểu, thay vì phải gọi phƣơng thức.
Ta có thể quá tải các toán tử sau:
Toán học: +,-,*,?,%.
70
Cộng trừ 1 ngôi: ++, --, -.
Quan hệ so sánh: ==,!=,>,<,>=,<=.
Ép kiểu: ()
Cú pháp khai báo nguyên mẫu một toán tử T:
public static KiểuTrảVề operator T (CácThamSố)
{
///các câu lệnh trong thân toán tử
}
Chú ý:
Tham số của toán tử phải là tham trị (không dùng các từ khóa ref, out).
Không đƣợc quá tải toán tử = (gán), && , || (and, or logic), ?: (điều kiện),
checked, unchecked, new, typeof, as, is.
[] không đƣợc xem là một toán tử.
Khi quá tải các toán tử dạng: +, *, / , % thì các toán tử +=, -=, *=, /= , %= cũng
tự động đƣợc quá tải.
Khi quá tải toán tử thì nên quá tải theo cặp đối ngẫu. Chẳng hạn, khi quá tải
toán tử == thì quá tải thêm toán tử !=...
Khi quá tải toán tử ==, != thì nên phát triển thêm các phƣơng thức Equals(),
GetHashCode() để đảm bảo luật “hai đối tượng bằng nhau theo toán tử = =
hoặc phương thức Equals sẽ có cùng mã băm”.
Khi định nghĩa toán tử ép kiểu ta phải chỉ ra đây là toán tử ép kiểu ngầm định
(implicit) hay tƣờng minh (explicit).
Cú pháp định nghĩa toán tử ép kiểu:
public static [implicit I explicit] operator KiểuTrảVềT (Type V)
trong đó Type V là biến cần ép sang kiểu KiểuTrảVềT.
Giả sử khi ta thiết kế lớp phân số (CPHANSO) có phƣơng thức cộng (Cong),
trừ (Tru), nhân (Nhan) và chia (Chia) 2 phân số.
Khi đó, để cộng 2 phân số a và b lƣu vào c:
c = a.Cong(b);
Tƣơng tự cho trƣờng hợp nhân
c = a.Nhan(b);
71
Cách này không thể hiện hết ý nghĩa là:
c = a + b;
c = a * b;
Vì vậy C# đã cung cấp cơ chế cài đặt phƣơng thức thông qua các ký hiệu phép
toán (operator).
public static TênLớp operator kýhiệu(TênLớptrái, TênLớpphải)
Ví dụ 5.12: Giả sử có lớp phân số (CPHANSO)
public static CPHANSO operator +(CPHANSO ps1, CPHANSO ps2)
{
//Cài đặt
}
Giả sử có 2 phân số a, b và phân số tổng c. Yêu cầu thực hiện nhƣ sau: c = a +
b;
Trong trƣờng hợp không dùng operator
class CPHANSO
{
private int tuso, mauso;
public CPHANSO(int t, int m)
{
tuso = t;
mauso = m;
}
public CPHANSO Cong(CPHANSO ps2)
{
int tu = tuso*ps2.mauso + ps2.tuso*mauso;
int mau = mauso*ps2.mauso;
CPHANSO c = new CPHANSO(tu, mau);
return c;
}
public void Xuat()
72
{
Console.WriteLine("{0}/{1}", tuso, mauso);
}
}
class Program
{
static void Main(string[] args)
{
CPHANSO a = new CPHANSO(3, 5);
a.Xuat();
CPHANSO b = new CPHANSO(1, 2);
b.Xuat();
CPHANSO c;
c = a.Cong(b);
Console.WriteLine("Ket qua: ");
c.Xuat();
}
}
Trong trƣờng hợp dùng operator
class CPHANSO
{
private int tuso, mauso;
public CPHANSO(int t, int m)
{
tuso = t; mauso = m;
}
public static CPHANSO operator +(CPHANSO ps1, CPHANSO ps2)
{
int tu = ps1.tuso*ps2.mauso + ps2.tuso*ps1.mauso;
73
int mau = ps1.mauso*ps2.mauso;
CPHANSO c = new CPHANSO(tu, mau);
return c;
}
public void Xuat()
{
Console.WriteLine("{0}/{1}", tuso, mauso);
}
}
class Program
{
static void Main(string[] args)
{
CPHANSO a = new CPHANSO(3, 5);
a.Xuat();
CPHANSO b = new CPHANSO(1, 2);
b.Xuat();
CPHANSO c;
c = a + b;
Console.WriteLine("Ket qua: ");
c.Xuat();
}
}
74
75
Bài tập
1. Tạo một class có tên là BAI_TAP_CLASS, có các hàm:
- Hàm TEN nhận vào một chuỗi chỉ họ và tên, giá trị trả lại của hàm là chuỗi chỉ
tên.
- Hàm NGTO nhận vào một số nguyên n, giá trị trả lại của hàm là true nếu n là số
nguyên tố; là false nếu n không là số nguyên tố.
2. Thiết kế form có:
- TextBox txtHoTen: nhập họ và tên.
- TextBox txtn: nhập số nguyên n.
- TextBox txtKQ: xuất kết quả.
- Button btnTEN: gọi hàm TEN xử lýchuỗi họ và tên, xuất kết quả vào txtKQ. -
Button btnNGTO: gọi hàm NGTO xử lý số nguyên n, xuất kết quả vào txtKQ. -
Button btnXoa: xóa trống tất cả các TextBox và đƣa con trỏ vào ô txtHoTen
- Button btnDung: dừng chƣơng trình.
3. Viết chƣơng trình xây dựng lớp TamGiac với dữ liệu là 3 cạnh của tam giác. Xây
dựng các thuộc tính (property) ChuVi, DienTich và các phƣơng thức kiểm tra kiểu
của tam giác (thƣờng, vuông, cân, vuông cân, đều).
4. Viết chƣơng trình xây dựng lớp HinhTruTron (hình trụ tròn) với dữ liệu chiều cao
và bán kính. Xây dựng các thuộc tính (property) DienTichDay (diên tích mặt đáy),
DienTichXungQuanh (diên tích mặt xung quanh), TheTich (thể tích).
-- oOo --
76
Bài 6
KẾ THỪA VÀ ĐA HÌNH
Mã bài: MĐ33-06
Giới thiệu:
Kế thừa là 1 cơ chế trong ngôn ngữ lập trình hƣớng đối tƣợng cho phép thể
hiện quan hệ đặc biệt hoá trong sơ đồ lớp bằng cách cho phép khai báo 1 lớp B là 1
lớp dẫn xuất từ lớp A (B là trƣờng hợp đặc biệt của A) khi đó B sẽ có tất cả các thuộc
tính và đặc điểm của A ngoài ra B có thể có thêm những thuộc tính mới, những hàm
kiểm tra ràng buộc mới, những hoạt động khởi tạo, cập nhật, cung cấp thông tin và xử
lý mới. Bài học này sẽ cung cấp cho học sinh những kỹ thuật xây dựng các lớp dẫn
xuất kế thừa từ lớp cơ sở, cũng nhƣ kỹ năng gọi các phƣơng thức của các lớp cơ sở và
điều khiển truy xuất các lớp có kế thừa. Ngoài ra còn vận dụng tính chất kế thừa vào
việc sử dụng lại thông qua các kiểu đa hình và phƣơng thức đa hình.
Mục tiêu:
Trình bày đƣợc các kiến thức về tính kế thừa và đa hình trên C#;
Sử dụng các kiến thức về lớp trừu tƣợng;
Vận dụng đƣợc các kiến thức và kỹ năng về các phƣơng thức, các thành phần
static;
Vận dụng đƣợc các kiến thức và kỹ năng về tham số và các phƣơng thức nạp
chồng;
Nghiêm túc, sáng tạo trong quá trình tiếp thu lý thuyết và áp dụng làm các bài
tập.
Nội dung chính:
6.1 Sự kế thừa
6.1.1. Quan hệ chuyên biệt hóa và tổng quát hóa
Các lớp và thể hiện của lớp 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ệ 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 hạn, quan hệ is-a (là một) là
một sự chuyên biệt hóa. Ví dụ, khi ta nói “Sơn dƣơng là một loài động vật, đại bàng
cũng là một loài động vật ”, thì có nghĩa là: “Sơn dƣơng và đại bàng là những loại
động vật chuyên biệt, chúng có những đặc điểm chung của động vật và ngoài ra
77
chúng có những đặc điểm phân biệt nhau”. Và nhƣ vậy, động vật là tổng quát hóa của
sơn dƣơng và đại bàng; sơn dƣơng và đại bàng là chuyên biệt hóa của động vật.
Trong C#, quan hệ chuyên biệt hóa, tổng quát hoá thƣờng đƣợc thể hiện thông
qua sự kế thừa. Bởi vì, thông thƣờng, khi hai lớp chia sẻ chức năng, dữ liệu với nhau,
ta trích ra các phần chung đó và đƣa vào lớp cơ sở chung để có thể nâng cao khả năng
sử dụng lại các mã nguồn chung, cũng nhƣ dễ dàng quản lý mã nguồn.
6.1.2. Khái niệm kế thừa
Kế thừa từ các lớp có từ trƣớc.
Lớp cơ sở LỚP CHA CSINHVIEN
Ích lợi: có thể tận dụng lại: (Base class) (Super class)
79
{}
}
class DANXUAT : COSO
{
private kiểu data3;
public override void Method1()
{}
public override void Method2()
{}
}
Ví dụ 6.1:
Nếu ta định nghĩa lớp ClassA và ClassB kế thừa từ ClassA nhƣ sau thì câu lệnh
x = x -1 sẽ bị báo lỗi:
ClassA.x is inaccessible due to its protection level.
class ClassA
{
int x = 5;
public void XuatX()
{
Console.WriteLine("{0}", x);
}
}
class ClassB: ClassA
{
public void GiamX()
{
x = x - 1; // Loi.
}
}
80
Nếu sửa lại khai báo int x = 5; thành protected int x = 5; hoặc public int x = 5;
thì sẽ không còn lỗi trên vì thành phần protected hoặc public của lớp cơ sở có thể
đƣợc truy cập trực tiếp trong lớp dẫn xuất (nhƣng không đƣợc truy cập trong một
phƣơng thức không thuộc lớp cơ sở và lớp dẫn xuất).
6.1.4. Gọi phƣơng thức tạo lập của lớp cơ sở
Vì lớp dẫn xuất 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. Nếu lớp cơ sở có một
phƣơng thức tạo lập mặc định (tức là không có phƣơng thức tạo lập hoặc phƣơng thức
tạo lập không có tham số) thì phƣơng thức tạo lập của lớp dẫn xuất đƣợc định nghĩa
nhƣ cách thông thƣờng. Nếu lớp cơ sở có phƣơng thức tạo lập có tham số thì lớp dẫn
xuất cũng phải định nghĩa phƣơng thức tạo lập có tham số theo cú pháp sau:
TênLớpCon(ThamSốLớpCon): TênLớpCơSở(ThamSốLớpCha)
{
// Khởi tạo giá trị cho các thành phần của lớp dẫn xuất
}
Chú ý: ThamSốLớpCon phải bao ThamSốLớpCha.
6.1.5. Định nghĩa phiên bản mới trong lớp dẫn xuất
Qua những phần trên chúng ta có nhận xét rằng, khi cần định nghĩa hai lớp mà
chúng có chung một vài đặc trƣng, chức năng thì những thành phần đó nên đƣợc đặt
vào một lớp cơ sở. Sau đó hai lớp này sẽ kế thừa từ lớp cơ sở đó và bổ sung thêm các
thành phần của riêng chúng. Ngoài ra, lớp dẫn xuất còn có quyền định nghĩa lại các
phƣơng thức đã kế thừa từ lớp cơ sở nhƣng không còn phù hợp với nó nữa.
Lớp dẫn xuất kế thừa hầu hết các thành viên của lớp cơ sở vì vậy trong bất kỳ
phƣơng thức nào của lớp dẫn xuất ta có thể truy cập trực tiếp đến các thành viên này
(mà không cần thông qua một đối tƣợng thuộc lớp cơ sở). Tuy nhiên, nếu lớp dẫn xuất
cũng có một thành phần X (biến hoặc phƣơng thức) nào đó trùng tên với thành viên
thuộc lớp cơ sở thì trình biên dịch sẽ có cảnh báo dạng nhƣ sau:
“keyword new is required on „LớpDẫnXuất.X‟ because
it hides inherited member on „LớpCơSở.X „”
bởi vì, trong lớp dẫn xuất, khi khai báo một thành phần trùng tên lớp thành
phần trong lớp cơ sở thì trình biên dịch hiểu rằng ngƣời dùng muốn che dấu các thành
viên của lớp cơ sở và yêu cầu ngƣời dùng đặt từ khóa new ngay câu lệnh khai báo
thành phần đó. Điều này có tác dụng che dấu thành phần kế thừa đó đối với các
phƣơng thức bên ngoài lớp dẫn xuất. Nếu phƣơng thức của lớp dẫn xuất muốn truy
81
cập đến thành phần X của lớp cơ sở thì phải sử dụng từ khóa base theo cú pháp:
base.X.
6.1.6. Điều khiển truy xuất
Một tham chiếu thuộc lớp cơ sở có thể trỏ đến một đối tƣợng thuộc lớp dẫn
xuất nhƣng nó chỉ đƣợc phép truy cập đến các thành phần đƣợc khai báo trong lớp cơ
sở. Với các lớp XeHoi, XeCar nhƣ trên, ta có thể định nghĩa hàm Main() nhƣ sau:
public static void Main()
{
XeCar c = new XeCar(150,"49A-4444", "Toyota", 24);
c.Xuat();
Console.WriteLine();
Console.WriteLine("Tham chieu cua lop co so XeHoi co
the tro den doi tuong thuoclop dan xuat XeCar");
Console.WriteLine("Nhung chi co the goi ham xuat tuong
ung voi XeHoi");
XeHoi h = c;
h.Xuat();
Console.ReadLine();
}
Kết quả chạy chƣơng trình:
83
6.2.2 Lớp Object
Tất cả các lớp trong C# đều đƣợc dẫn xuất từ lớp Object. Lớp Object là lớp gốc
trên cây phân cấp kế thừa, nó cung cấp một số phƣơng thức mà các lớp con có thể ghi
đè (override) nhƣ:
Phƣơng thức Ý nghĩa
Equals() Kiểm tra hai đối tƣợng có tƣơng đƣơng nhau
không
GetHashCode() Cho phép đối tƣợng cung cấp làm Hash riêng
để sử dụng trong kiểu tập hợp.
GetType() Trả về kiểu đối tƣợng.
ToString() Trả về kiểu đối tƣợng.
Finalize() Xóa đối tƣợng trong bộ nhớ.
MemberwiseClone() Tạo copy của đối tƣợng.
Trong những ví dụ trƣớc ta đã thực hiện việc ghi đè lên phƣơng thức ToString()
và Equals() của lớp Object.
6.3 Lớp trừu tƣợng(abstract)
Trong các ví dụ về phƣơng thức ảo trên, nếu lớp dẫn xuất không định nghĩa lại
phƣơng thức ảo của lớp cơ sở thì nó đƣợc kế thừa nhƣ một phƣơng thức thông
thƣờng. Tức là lớp dẫn xuất không nhất thiết phải định nghĩa lại phƣơng thức ảo.
Để bắt buộc tất cả các lớp dẫn xuất phải định nghĩa lại (override) một phƣơng
thức của lớp cơ sở ta phải đặt từ khóa abstract trƣớc phƣơng thức đó và phƣơng thức
đó đƣợc gọi là phƣơng thức trừu tƣợng. Trong phần thân của phƣơng thức trừu tƣợng
không có câu lệnh nào, nó chỉ tạo một tên phƣơng thức và đánh dấu rằng nó phải
đƣợc thi hành trong lớp dẫn xuất. Lớp chứa phƣơng thức trừu tƣợng đƣợc gọi là lớp
trừu tƣợng.
Cú pháp khai báo phƣơng thức trừu tƣợng:
abstract public void TênPhƣơngThức( );
Phƣơng thức trừu tƣợng phải đƣợc đặt trong lớp trừu tƣợng. Lớp trừu tƣợng có
từ khóa abstract đứng trƣớc.
Ví dụ 6.2:
Xây dựng lớp HinhHoc với phƣơng thức tính chu vi, diện tích là phƣơng thức
trừu tƣợng hoặc phƣơng thức ảo. Sau đó định nghĩa các lớp HinhChuNhat (hình chữ
84
nhật), HinhTron (hình tròn) kế thừa từ lớp HinhHọc với các thành phần dữ liệu và
phƣơng thức tính chu vi, diện tích cụ thể của từng loại đối tƣợng.
// lop hinh hoc (truu tuong)
abstract public class HinhHoc
{
abstract public double DienTich();
virtual public double ChuVi() { return 0;}
}
// lop hinh tron ke thua tu lop hinh hoc
public class HinhTron : HinhHoc
{
double _bankinh;
public double BanKinh
{
get{ return _bankinh;}
set{ _bankinh = value;}
}
public override double DienTich()
{
return _bankinh*_bankinh*3.1416;
}
public override double ChuVi()
{
return _bankinh*2*3.1416;
}
}
// lop hinh chu nhat ke thua tu lop hinh hoc
86
n.ChieuDai = 4;
n.ChieuRong = 3;
h = n;
Console.WriteLine("Thong tin ve hinh chu nhat ");
Console.WriteLine("Chu vi hinh chu nhat:{0}",
h.ChuVi());
Console.WriteLine("Dien tich hinh chu nhat:{0}",
h.DienTich());
Console.ReadLine();
}
}
Trong lớp HinhHoc ở ví dụ trên, ta khai báo phƣơng thức tính diện tích là một
phƣơng thức trừu tƣợng (không có phần cài đặt mã của phƣơng thức vì chƣa biết đối
tƣợng hình thuộc dạng nào nên không thể tính diện tích của nó). Nhƣ vậy, lớp
HinhHoc là một lớp trừu tƣợng.
abstract public double DienTich();
Trong lớp trên, ta cũng khai báo một phƣơng thức tính chu vi là phƣơng thức
ảo với mục đích minh họa rằng trong lớp trừu tƣợng có thể có phƣơng thức ảo (hoặc
bất cứ một thành phần nào khác của một lớp). Vì đây là một phƣơng thức không trừu
tƣợng nên phải có phần cài đặt mã bên trong thân phƣơng thức.
Các lớp HinhChuNhat, HinhTron kế thừa từ lớp HinhHoc nên chúng buộc phải
cài đặt lại phƣơng thức tính diện tích.
Trong hàm Main(), ta tạo ra đối tƣợng n thuộc lớp HinhChuNhat và đối tƣợng t
thuộc lớp HinhTron. Khi tham chiếu h thuộc lớp HinhHoc trỏ tới đối tƣợng n (tƣơng
tự với đối tƣợng t), nó có thể gọi đƣợc phƣơng thức tính diện tích của lớp
HinhChuNhat (tƣơng tự lớp HinhTron), điều này chứng tỏ phƣơng thức trừu tƣợng
cũng có thể dùng với mục đích đa hình.
Chú ý: Phân biệt giữa từ khóa new và override
• Từ khóa override dùng để định nghĩa lại (ghi đè) phƣơng thức ảo (virtual)
hoặc phƣơng thức trừu tƣợng (abstract) của lớp cơ sở, nó đƣợc dùng với mục đích đa
hình.
• Từ khóa new để che dấu thành viên của lớp cơ sở trùng tên với thành viên của
lớp dẫn xuất.
87
88
Bài tập
1. Hãy xây dựng cây phân cấp các lớp đối tƣợng sau: XeToyota, XeDream,
XeSpacy, Xe_BMW, XeFiat, XeDuLich, Xe_May, Xe?
2. Hãy xây dựng các lớp đối tƣợng trong câu hỏi 1, thiết lập các quan hệ kế thừa
dựa trên cây kế thừa mà bạn xây dựng. Mỗi đối tƣợng chỉ cần một thuộc tính là
myNane để cho biết tên của nó (nhƣ XeToyota thì myName là “Toi la Toyota”...).
Các đối tƣợng có phƣơng thức Who() cho biết giá trị myName của nó. Hãy thực thi sự
đa hình trên các lớp đó. Cuối cùng tạo một lớp Tester với hàm Main() để tạo một
mảng các đối tƣợng Xe, đƣa từng đối tƣợng cụ thể vào mảng đối tƣợng Xe, sau đó
cho lặp từng đối tƣợng trong mảng để nó tự giới thiệu tên (bằng cách gọi hàm Who()
của từng đối tƣợng).
3. Xây dựng các lớp đối tƣợng hình học nhƣ: điểm, đoạn thẳng, đƣờng tròn, hình
chữ nhật, hình vuông, tam giác, hình bình hành, hình thoi. Mỗi lớp có các thuộc tính
riêng để xác định đƣợc hình vẽ biểu diễn của nó nhƣ đoạn thẳng thì có điểm đầu,
điểm cuối.... Mỗi lớp thực thi một phƣơng thức Draw() phủ quyết phƣơng thức
Draw() của lớp cơ sở gốc của các hình mà nó dẫn xuất. Hãy xây dựng lớp cơ sở của
các lớp trên và thực thi đa hình với phƣơng thức Draw(). Sau đó tạo lớp Tester cùng
với hàm Main() để thử nghiệm đa hình giống nhƣ bài tập 1 ở trên.
89
Bài 7
THỰC THI GIAO DIỆN
Mã bài: MĐ33-07
Giới thiệu:
Giao diện là một dạng của lớp trừu tƣợng đƣợc sử dụng với mục đích hỗ trợ
tính đa hình. Trong giao diện không có bất cứ một cài đặt nào, chỉ có nguyên mẫu của
các phƣơng thức, chỉ mục, thuộc tính mà một lớp khác khi kế thừa nó thì phải có cài
đặt cụ thể cho các thành phần này (tức là lớp kế thừa giao diện tuyên bố rằng nó hỗ
trợ các phƣơng thức, thuộc tính, chỉ mục đƣợc khai báo trong giao diện).
Khi một lớp kế thừa một giao diện ta nói rằng lớp đó thực thi (implement) giao
diện.
Mục tiêu:
Trình bày đƣợc các kiến thức về thực thi giao diện;
Có đƣợc kiến thức và kỹ năng về các thành phần và các mở rộng của giao diện;
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
7.1 Thực thi giao diện (Interface)
Giao diện là một dạng của lớp trừu tƣợng đƣợc sử dụng với mục đích hỗ trợ
tính đa hình. Trong giao diện không có bất cứ một cài đặt nào, chỉ có nguyên mẫu của
các phƣơng thức, chỉ mục, thuộc tính mà một lớp khác khi kế thừa nó thì phải có cài
đặt cụ thể cho các thành phần này (tức là lớp kế thừa giao diện tuyên bố rằng nó hỗ
trợ các phƣơng thức, thuộc tính, chỉ mục đƣợc khai báo trong giao diện).
Khi một lớp kế thừa một giao diện ta nói rằng lớp đó thực thi (implement) giao
diện.
Thực thi giao diện
Ta dùng từ khóa interface để khai báo một giao diện với cú pháp sau:
[MứcĐộTruyCập] interface TênGiaoDiện [:CácGiaoDiệnCơSở]
{
Nội dung
}
MứcĐộTruyCập: public hoặc internal.
CácGiaoDiệnCơSở: danh sách các interface khác mà nó kế thừa.
90
Về mặt cú pháp, một giao diện giống nhƣ một lớp chỉ có phƣơng thức trừu
trƣợng.
Nó có thể chứa khai báo của phƣơng thức, thuộc tính, chỉ mục, sự kiện (events)
nhƣng không đƣợc chứa biến dữ liệu. Khi kế thừa một giao diện ta phải thực thi mọi
phƣơng thức, thuộc tính,… của giao diện.
Chú ý:
• Mặc định, tất cả các thành phần khai báo trong giao diện đều là public. Nếu
có từ khóa public đứng trƣớc sẽ bị báo lỗi. Các thành phần trong giao diện chỉ là các
khai báo, không có phần cài đặt mã.
• Một lớp có thể kế thừa một lớp khác đồng thời kế thừa nhiều giao diện.
Hủy đối tƣợng
Ngôn ngữ C# cung cấp cơ chế thu dọn rác tự động (garbage collection) và do
vậy không cần phải khai báo tƣờng minh các phƣơng thức hủy. Tuy nhiên, khi làm
việc với các đoạn mã không đƣợc quản lý thì cần phải khai báo tƣờng minh các
phƣơng thức hủy để giải phóng các tài nguyên. C# cung cấp ngầm định một phƣơng
thức để thực hiện điều khiển công việc này, phƣơng thức đó là Finalize() hay còn gọi
là bộ kết thúc. Phƣơng thức Finalize() này sẽ đƣợc gọi một cách tự động bởi cơ chế
thu dọn khi đối tƣợng bị hủy.
Phƣơng thức Finalize() chỉ giải phóng các tài nguyên mà đối tƣợng nắm giữ, và
không tham chiếu đến các đối tƣợng khác. Nếu với những đoạn mã bình thƣờng tức là
chứa các tham chiếu kiểm soát đƣợc thì không cần thiết phải tạo và thực thi phƣơng
thức Finalize(). Ta chỉ làm điều này khi xử lý các tài nguyên không kiểm soát đƣợc.
Ta không bao giờ gọi một phƣơng thức Finalize() của một đối tƣợng một cách
trực tiếp, ngoại trừ gọi phƣơng thức này của lớp cơ sở khi ở bên trong phƣơng thức
Finalize() của lớp đang định nghĩa. Trình thu dọn sẽ tự động thực hiện việc gọi
Finalize() cho chúng ta.
a) Cách Finalize thực hiện
Bộ thu dọn duy trì một danh sách những đối tƣợng có phƣơng thức Finalize().
Danh sách này đƣợc cập nhật mỗi lần khi đối tƣợng cuối cùng đƣợc tạo ra hay
bị hủy. Khi một đối tƣợng trong danh sách kết thúc của bộ thu dọn đƣợc chọn đầu
tiên. Nó sẽ đƣợc đặt vào hàng đợi (queue) cùng với những đối tƣợng khác đang chờ
kết thúc. Sau khi phƣơng thức Finalize() của đối tƣợng thực thi bộ thu dọn sẽ gom lại
đối tƣợng và cập nhật lại danh sách hàng đợi, cũng nhƣ là danh sách kết thúc đối
tƣợng.
91
b) Bộ hủy của C#
Ta khai báo một phƣơng thức hủy trong C# nhƣ sau:
Class1()
{
}
Tuy nhiên, trong ngôn ngữ C# thì cú pháp khai báo trên là một shortcut liên kết
đến một phƣơng thức kết thúc Finalize() đƣợc kết với lớp cơ sở, do vậy khi viết
~Class1()
{
// Thực hiện một số công việc
}
Cũng tương tự như viết :
Class1.Finalize()
{
// Thực hiện một số công việc
base.Finalize();
}
Do sự tƣơng tự nhƣ trên nên khả năng dẫn đến sự lộn xộn nhầm lẫn là không
tránh khỏi, nên ta phải tránh viết các phƣơng thức hủy và viết các phƣơng thức
Finalize() tƣờng minh nếu có thể đƣợc.
c) Phƣơng thức Dispose
Nhƣ chúng ta đã biết thì việc gọi một phƣơng thức kết thúc Finalize() trong C#
là không hợp lệ, vì phƣơng thức này đƣợc thực hiện một cách tự động bởi bộ thu dọn
rác. Nếu muốn xử lý các tài nguyên không kiểm soát (nhƣ xử lý các handle của tập
tin), đóng hay giải phóng nhanh chóng bất cứ lúc nào thì ta nên thực thi giao diện
IDisposable. Giao diện IDisposable yêu cầu những lớp thực thi nó định nghĩa một
phƣơng thức tên là Dispose() để thực hiện công việc dọn dẹp. Ý nghĩa của phƣơng
thức Dispose() là cho phép chƣơng trình thực hiện các công việc dọn dẹp hay giải
phóng tài nguyên mong muốn mà không phải chờ cho đến khi phƣơng thức Finalize()
đƣợc gọi.
Khi chúng ta cung cấp một phƣơng thức Dispose() thì phải ngƣng bộ thu dọn
gọi phƣơng thức Finalize() trong đối tƣợng của chúng ta. Để ngƣng bộ thu dọn, chúng
ta gọi một phƣơng thức tĩnh của lớp GC (garbage collector) là GC.SuppressFinalize()
92
và truyền tham số là tham chiếu this của đối tƣợng. Và sau đó phƣơng thức Finalize()
sử dụng để gọi phƣơng thức Dispose() nhƣ đoạn mã sau:
public void Dispose()
{
// Thực hiện công việc dọn dẹp
// Yêu cầu bộ thu dọn GC trong thực hiện kết thúc
GC.SuppressFinalize( this );
}
public override void Finalize()
{
Dispose();
base.Finalize();
}
Ví dụ 7.1:
using System;
public class Mang: IDisposable
{
int []Data;
public int Size;
public Mang(int n)
{
Data = new int[n];
Size = n;
}
public void Dispose()
{
// Thuc hien viec don dep
Console.WriteLine("Huy mang");
Data =new int [0];
Size = 0;
93
// yeu cau bo thu don GC thuc hien ket thuc
GC.SuppressFinalize( this );
}
public void Nhap()
{
for( int i =0; i < Size; i++)
{
Console.WriteLine("nhap phan tu thu {0}
",i);
Data[i] =
int.Parse(Console.ReadLine());
}
}
public void Xuat()
{
for(int i = 0; i < Size; i++)
{
Console.Write("{0} \t",Data[i]);
}
}
}
class App
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Test()
{
94
Mang A = new Mang(5);
A.Nhap();
A.Xuat();
A.Dispose();
}
static void Main(string[] args)
{
Test();
Console.ReadLine();
}
}
d) Phƣơng thức Close
Khi xây dựng các đối tƣợng, chúng ta muốn cung cấp cho ngƣời sử dụng
phƣơng thức Close(), vì phƣơng thức Close() có vẻ tự nhiên hơn phƣơng thức
Dispose() trong các đối tƣợng có liên quan đến xử lý tập tin. Ta có thể xây dựng
phƣơng thức Dispose() với thuộc tính là private và phƣơng thức Close() với thuộc
tính public. Trong phƣơng thức Close() đơn giản là gọi thực hiện phƣơng thức
Dispose().
e) Câu lệnh using
Khi xây dựng các đối tƣợng chúng ta không thể chắc chắn đƣợc rằng ngƣời sử
dụng có thể gọi hàm Dispose(). Và cũng không kiểm soát đƣợc lúc nào thì bộ thu dọn
GC thực hiện việc dọn dẹp. Do đó để cung cấp khả năng mạnh hơn để kiểm soát việc
giải phóng tài nguyên thì C# đƣa ra cú pháp chỉ dẫn using, cú pháp này đảm bảo
phƣơng thức Dispose() sẽ đƣợc gọi sớm nhất có thể đƣợc. Ý tƣởng là khai báo các đối
tƣợng với cú pháp using và sau đó tạo một phạm vi hoạt động cho các đối tƣợng này
trong khối đƣợc bao bởi dấu ({}). Khi khối phạm vi này kết thúc, thì phƣơng thức
Dispose() của đối tƣợng sẽ đƣợc gọi một cách tự động.
using System.Drawing;
class Tester
{
public static void Main()
{
95
using ( Font Afont = new Font(“Arial”,10.0f))
{
// Đoạn mã sử dụng AFont
.......
}// Trình biên dịch sẽ gọi Dispose để giải phóng AFont
Font TFont = new Font(“Tahoma”,12.0f);
using (TFont)
{
// Đoạn mã sử dụng TFont
.......
}// Trình biên dịch gọi Dispose để giải phóng TFont
}
}
7.1.1. Thực thi nhiều giao diện
Các lớp có thể thực thi nhiều giao diện, đây là cách để thực hiện đa kế thừa
trong C#.
Ví dụ 7.2:
Tạo một giao diện tên là Istoreable với các phƣơng thức Write() để lƣu nội
dung của đối tƣợng vào file và phƣơng thức Read() để đọc dữ liệu từ file. Sau đó ta
tạo lớp Document thực thi giao diện Istorable để các đối tƣợng thuộc lớp này có thể
đọc từ cơ sở dữ liệu hoặc lƣu trữ vào cơ sở dữ liệu. Việc mở file đƣợc thực hiện thông
qua đối tƣợng fs thuộc lớp FileStream, việc ghi và đọc file thông qua đối tƣợng thuộc
các lớp StreamWriter và StreamReader. Đồng thời lớp Document cũng thực thi một
giao diện khác tên là Iencryptable, giao diện này có hai phƣơng thức là mã hóa
(Encrypt( ))và giải mã (Decrypt( )):
using System;
using System.IO;
// Khai bao giao dien
interface IStorable
{
// mac dinh cac khai bao phuong thuc la public. Khong cai dat gi
96
void Read(string FileName);
void Write(string FileName);
string Data { get; set; } }
interface IEncryptable
{
void Encrypt( );
void Decrypt( );
}
// Lop Document thuc thi giao dien IStorable
public class Document : IStorable, IEncryptable
{
string S;
public Document(string str)
{
S = str;
}
97
while((text = sr.ReadLine())!=null)
{
S = S + text;
}
//Dong luong va dong file
sr.Close();
fs.Close();
}
// thuc thi phuong thuc Write cua giao dien IStorable
public void Write(string FileName)
{
//Mo file de ghi du lieu
FileStream fs ;
fs = new
fileStream(FileName,FileMode.OpenOrCreate);
//Tao luong ghi du lieu vao file
StreamWriter sw = new StreamWriter(fs);
//ghi chuoi S ra file
sw .WriteLine(S);
//dong luong va dong file
sw .Close();
fs.Close();
}
// thuc thi thuoc tinh Data cua giao dien IStorable
public string Data
{
get { return S; }
set { S = value; }
}
98
// thuc thi phuong thuc Encrypt cua giao dien IEncryptable
public void Encrypt()
{
string KQ ="";
for (int i = 0 ; i< S.Length; i++)
KQ = KQ + (char)((int)S[i] + 5);
S = KQ;
}
// thuc thi phuong thuc Encrypt cua giao dien IEncryptable
public void Decrypt()
{
string KQ ="";
for (int i = 0 ; i< S.Length; i++)
KQ= KQ + (char)((int)S[i] - 5);
S = KQ;
}
}
// Thu nghiem chuong chinh
public class Tester
{
static void Main( )
{
string FileName = "F:\\datafile.txt";
Document doc = new Document("THIEU NU la viet tat cua tu THIEU NU
TINH");
doc.Write(FileName);
doc.Read(FileName);
Console.WriteLine("Du lieu trong file: {0}",
doc.Data);
Console.WriteLine("Du lieu sau khi ma hoa:");
99
doc.Encrypt();
Console.WriteLine(doc.Data);
Console.WriteLine("Du lieu sau khi giai ma:");
doc.Decrypt();
Console.WriteLine(doc.Data);
Console.ReadLine();
}
}
Vì giao diện là một dạng lớp cơ sở nên ta có thể cho một tham chiếu kiểu giao
diện trỏ tới đối tƣợng thuộc lớp thực thi giao diện và gọi những phƣơng thức cần thiết.
Chẳng hạn, trong hàm Main() trên ta có thể thay câu lệnh: doc.Read(FileName);
bằng hai câu lệnh sau:
IStorable isDoc = doc; //(IStorable) doc;
isDoc.Read(FileName);
7.1.2. Mở rộng giao diện
Ta cũng có thể mở rộng giao diện bằng cách bổ sung những thành viên hay
phƣơng thức mới. Chẳng hạn, ta có thể mở rộng giao tiếp IStorable thành
IStorableAndCompressible bằng cách kế thừa từ giao tiếp Istorable bổ sung các
phƣơng thức nén file và giải nén file:
interface IStorableAndCompressible : IStorable
{
// bo sung them phuong thuc nen va giai nen
void Compress( );
void Decompress( );
}
7.1.3. Kết hợp giao diện
Thay vì lớp Document thực thi hai giao diện IStorable, IEncryptable , ta có thể
tạo kết hợp hai giao diện này thành một giao diện mới là IStorableAndEncryptable.
Sau đó lớp Document thực thi giao diện mới này:
interface IstorableAndEncryptable: IStorable, IEncryptable
{
100
//Có thể bổ sung thêm các phương thức, thuộc tính… mới
}
public class Document : IStorableAndEncryptable
{
…
}
7.2 Kiểm tra đối tƣợng có hỗ trợ giao diện hay không bằng toán tử is
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 bằng cách dùng toán tử is. Nếu đối tƣợng không hỗ trợ giao diện
mà ta cố tình ép kiểu thì có thể xảy ra biệt lệ (lỗi khi thực thi chƣơng trình).
Ví dụ 7.3: Kiểm tra đối tƣợng doc có hỗ trợ giao diện IStorable hay không, nếu
có thì ép sang kiểu IStorable và gọi phƣơng thức Read().
static void Main( )
{
Document doc = new Document("Test Document");
// chi ep sang kieu Istorable neu doi tuong co ho tro
if (doc is IStorable)
{
IStorable isDoc = (IStorable) doc;
isDoc.Read( );
}
}
7.3 Thực thi các giao diện tƣờng minh: Icomparer, IComparable (giao diện so
sánh) và ArrayList
• Thuộc name space System.Conllections
• Khi thi công giao diện IComparer cần cài đặt hàm mô tả sự so sánh hai đối
tƣợng obj1 và obj2: int Compare(Objerct obj1, Object obj2)
• Khi thi công giao diện IComparable cần cài đặt hàm mô tả sự so sánh của đối
tƣợng hiện tại với một đối tƣợng obj2 khác. int CompareTo(Object obj2)
• ArrayList có các chức năng của cả mảng và danh sách liên kết nhƣ:
Một vấn đề hạn chế của kiểu dữ liệu mảng là kích thƣớc cố định. Nếu chúng ta
101
không biết trƣớc số lƣợng đối tƣợng trong một mảng sẽ đƣợc lƣu giữ, thì sẽ khó khăn
vì có thể chúng ta khai báo kích thƣớc của mảng quá nhỏ (vƣợt quá kích thƣớc lƣu trữ
của mảng) hoặc là kích thƣớc quá lớn (dẫn đến lãng phí bộ nhớ). Chƣơng trình của
chúng ta có thể hỏi ngƣời dùng về kích thƣớc, hoặc thu những input từ trong một web
site.Tuy nhiên việc xác định số lƣợng của đối tƣợng trong những session có tính chất
tƣơng tác động là không thích hợp. Việc sử dụng mảng có kích thƣớc cố định là
không thích hợp cũng nhƣ là chúng ta không thể đoán trƣớc đƣợc kích thƣớc của
mảng cần thiết.
Lớp ArrayList là một kiểu dữ liệu mảng mà kích thƣớc của nó đƣợc gia tăng
một cách động theo yêu cầu. ArrayList cung cấp một số phƣơng thức và những thuộc
tính cho những thao tác liên quan đến mảng. Một vài phƣơng thức và thuộc tính quan
trọng của ArrayList đƣợc liệt kê trong bảng nhƣ sau
Tên Loại Ý nghĩa
Count Thuộc Số phần tử hiện có
tính
Add Hàm Thêm một phần tử vào cuối
103
Bài tập
1. Hãy viết một giao diện khai báo một thuộc tính ID chứa chuỗi giá trị. Viết một lớp
Employee thực thi giao diện đó.
2. Đọan mã nguồn sau đây có lỗi hãy sử lỗi và hãy cho biết tại sao có lỗi này. Sau khi
sửa lỗi hãy viết một lớp Circle thực thi giao diện này?
public interface Idimensions
{
long width; long height; double Area(); double Circumference(); int Side();
}
3. Chƣơng trình sau đây có lỗi hãy sử lỗi, biên dịch và chạy lại chƣơng trình? Giải
thích tại sao chƣơng trình có lỗi.
using System;
interface IPoint {
// Property signatures: int x {
get;
set;
}
int y {
get;
set;
}
}
class MyPoint : IPoint
{
private int myX;
private int myY;
public MyPoint(int x, int y)
{
myX =x;
myY = y;
104
}
// Property implementation: public int x{
get
{
return myX;
}
set
{
myX = value;
}
}
public int y
{
get
{
return myY;
}
set
{
myY = value;
}
}
}
Class MainClass
{
private static void PrintPoint(IPoint p)
{
Console.WriteUne("x={0}, y={1}", p.x, p.y);
}
105
public static void Main()
{
MyPoint p = new MyPoint(2,3); Console.Write("My Point: ");
PrintPoint(p);
IPoint p2 = new IPoint();
PrintPoint(p2);
}
}
4. Xây dựng một giao diện IDisplay có khai báo thuộc tính Name kiểu chuỗi. Hãy
viết hai lớp Dog và Cat thực thi giao diện IDisplay, cho biết thuộc tính Name là tên
của đối tƣợng.
106
Bài 8
CƠ CHẾ ỦY QUYỀN - SỰ KIỆN
Mã bài: MĐ33-08
Giới thiệu:
Trong lập trình chúng ta thƣờng đối diện với tình huống là khi chúng ta
muốn thực hiện một hành động nào đó, nhƣng hiện tại thì chƣa xác định đƣợc
chính xác phƣơng thức hay sự kiện trong đối tƣợng. Ví dụ nhƣ một nút lệnh
button biết rằng nó phải thông báo cho vài đối tƣợng khi nó đƣợc nhấn, nhƣng
nó không biết đối tƣợng hay nhiều đối tƣợng nào cần đƣợc thông báo. Tốt hơn
việc nối nút lệnh với đối tƣợng cụ thể, chúng ta có thể kết nối nút lệnh đến một
cơ chế ủy quyền và sau đó thì chúng ta thực hiện việc ủy quyền đến phƣơng
thức cụ thể khi thực thi chƣơng trình.
Trong thời kỳ đầu của máy tính, chƣơng trình đƣợc thực hiện theo trình tự
xử lý từng bƣớc tuần tự cho đến khi hoàn thành, và nếu ngƣời dùng thực hiện
một sự tƣơng tác thì sẽ làm hạn chế sự điều khiển hoạt động khác của chƣơng
trình cho đến khi sự tƣơng tác với ngƣời dùng chấm dứt.
Tuy nhiên, ngày nay với mô hình lập trình giao diện ngƣời dùng đồ họa
(GUI: Graphical User Interface) đòi hỏi một cách tiếp cận khác, và đƣợc biết
nhƣ là lập trình điều khiển sự kiện (event-driven programming). Chƣơng trình
hiện đại này đƣa ra một giao diện tƣơng tác với ngƣời dùng và sau đó thì chờ
cho ngƣời sử dụng kích hoạt một hành động nào đó. Ngƣời sử dụng có thể thực
hiện nhiều hành động khác nhau nhƣ: chọn các mục chọn trong menu, nhấn một
nút lệnh, cập nhật các ô chứa văn bản,...Mỗi hành động nhƣ vậy sẽ dẫn đến một
sự kiện (event) đƣợc sinh ra. Một số các sự kiện khác cũng có thể đƣợc xuất
hiện mà không cần hành động trực tiếp của ngƣời dùng. Các sự kiện này xuất
hiện do các thiết bị nhƣ đồng hồ của máy tính phát ra theo chu kỳ thời gian, thƣ
điện tử đƣợc nhận, hay đơn giản là báo một hành động sao chép tập tin hoàn
thành,...
Một sự kiện đƣợc đóng gói nhƣ một ý tƣởng “chuyện gì đó xảy ra‟ và chƣơng
trình phải đáp ứng lại với sự kiện đó. Cơ chế sự kiện và ủy quyền gắn liền với nhau, bởi
vì khi một sự kiện xuất hiện thì cần phải phân phát sự kiện đến trình xử lý sự kiện tƣơng
ứng. Thông trƣờng một trình xử lý sự kiện đƣợc thực thi trong C# nhƣ là một sự ủy
quyền.
Ủ y quyền cho phép một lớp có thể yêu cầu một lớp khác làm một công việc nào
đó, và khi thực hiện công việc đó thì phải báo cho lớp biết. Ủy quyền cũng co thể đƣợc
sử dụng để xác nhận những phƣơng thức chỉ đƣợc biết lúc thực thi chƣơng trình, và
chúng ta sẽ tìm hiểu kỹ vấn đề này trong phần chính của chƣơng.
Mục tiêu:
Trình bày đƣợc kiến thức về cơ chế ủy quyền trong C# và cách sử lý sự kiện;
Sử dụng cơ chế ủy quyền để thi hành với mảng ủy quyền;
Nghiêm túc, tỉ mỉ trong học lý thuyết và làm bài tập.
Nội dung chính:
8.1 Ủy quyền (delegate)
Trong ngôn ngữ C#, ủy quyền là lớp đối tƣợng đầu tiên (first-class
object), đƣợc hỗ trợ đầy đủ bởi ngôn ngữ lập trình. Theo kỹ thuật thì ủy quyền
là kiểu dữ liệu tham chiếu đƣợc dùng để đóng gói một phƣơng thức với tham số
và kiểu trả về xác định. Chúng ta có thể đóng gói bất cứ phƣơng thức thích hợp
nào vào trong một đối tƣợng ủy quyền. Trong ngôn ngữ C++ và những ngôn
ngữ khác, chúng ta có thể làm đƣợc điều này bằng cách sử dụng con trỏ hàm
(function pointer) và con trỏ đến hàm thành viên. Không giống nhƣ con trỏ hàm
nhƣ trong C/C++, ủy quyền là hƣớng đối tƣợng, kiểu dữ liệu an toàn (type-safe)
và bảo mật.
Một điều thú vị và hữu dụng của ủy quyền là nó không cần biết và cũng
không quan tâm đến những lớp đối tƣợng mà nó tham chiếu tới. Điều cần quan
tâm đến những đối tƣợng đó là các đối mục của phƣơng thức và kiểu trả về phải
phù hợp với đối tƣợng ủy quyền khai báo.
Để tạo một ủy quyền ta dùng từ khóa delegate theo sau là kiểu trả về tên
phƣơng thức đƣợc ủy quyền và các đối mục cần thiết:
public delegate int WhichIsFirst(object obj1, object obj2);
Khai báo trên định nghĩa một ủy quyền tên là WhichlsFirst, nó sẽ đóng
gói bất cứ phƣơng thức nào lấy hai tham số kiểu object và trả về giá trị int.
Một khi mà ủy quyền đƣợc định nghĩa, chúng ta có thể đóng gói một phƣơng
thức thành viên bằng việc tạo một thể hiện của ủy quyền này, truyền vào trong
một phƣơng thức có khai báo kiểu trả về và các đối mục cần thiết.
Lƣu ý:
Từ phần này về sau chúng ta quy ƣớc có thể sử dụng qua lại giữa hai từ uỷ
quyền và delegate với nhau.
8.1.1 Sử dụng ủy quyền để xác nhận phƣơng thức lúc thực thi
Ủ y quyền nhƣ chúng ta đã biết là đƣợc dùng để xác định những loại phƣơng
thức có thể đƣợc dùng để xử lý các sự kiện và để thực hiện callback trong
chƣơng trình ứng dụng. Chúng cũng có thể đƣợc sử dụng để xác định các
phƣơng thức tĩnh và các instance của phƣơng thức mà chúng ta không biết trƣớc
cho đến khi chƣơng trình thực hiện.
Giả sử minh họa nhƣ sau, chúng ta muốn tạo một lớp chứa đơn giản gọi là
Pair lớp này lƣu giữ và sắp xếp hai đối tƣợng đƣợc truyền vào cho chúng. Tạm
thời lúc này chúng ta cũng không thể biết loại đối tƣợng mà một Pair lƣu giữ.
Nhƣng bằng cách tạo ra các phƣơng thức bên trong các đối tƣợng này thực hiện
việc sắp xếp và đƣợc ủy quyền, chúng ta có thể ủy quyền thực hiện việc sắp thứ
tự cho chính bản thân của đối tƣợng đó.
Những đối tƣợng khác nhau thì sẽ sắp xếp khác nhau. Ví dụ, một Pair chứa
các đối tƣợng đếm có thể đƣợc sắp xếp theo thứ tự số, trong khi đó một Pair nút
lệnh button có thể đƣợc sắp theo thứ tự alphabe tên của chúng. Mong muốn của
ngƣời tạo ra lớp Pair là những đối tƣợng bên trong của Pair phải có trách nhiệm
cho biết thứ tự của chúng cái nào là thứ tự đầu tiên và thứ hai. Để làm đƣợc điều
này, chúng ta phải đảm bảo rằng các đối tƣợng bên trong Pair phải cung cấp một
phƣơng thức chỉ ra cho chúng ta biết cách sắp xếp các đối tƣợng.
Chúng ta định nghĩa phƣơng thức yêu cầu bằng việc tạo một ủy quyền, ủy
quyền này định nghĩa ký pháp và kiểu trả về của phƣơng thức đối tƣợng (nhƣ
button) để cung cấp và cho phép Pair xác định đối tƣợng nào đến trƣớc đầu tiên
và đối tƣợng nào là thứ hai.
Lớp Pair định nghĩa một ủy quyền, WhichIsFirst. Phƣơng thức Sort sẽ lấy
một tham số là thể hiện của WhichIsFirst. Khi một đối tƣợng Pair cần biết thứ tự
của những đối tƣợng bên trong của nó thì nó sẽ yêu cầu ủy quyền truyền vào hai
đối tƣợng chứa trong nó nhƣ là tham số. Trách nhiệm của việc xác định thứ tự
của hai đối tƣợng đƣợc trao cho phƣơng thức đóng gói bởi ủy quyền.
Để kiểm tra thực hiện cơ chế ủy quyền, chúng ta sẽ tạo ra hai lớp, lớp Cat và
lớp Student. Hai lớp này có ít điểm chung với nhau, ngoại trừ cả hai thực thi
những phƣơng thức đƣợc đóng gói bởi WhichIsFirst. Do vậy cả hai đối tƣợng
này có thể đƣợc lƣu giữ bên trong của đối tƣợng Pair.
Trong chƣơng trình thử nghiệm này chúng ta sẽ tạo ra hai đối tƣợng Student
và hai đối tƣợng Cat và lƣu chúng vào mỗi một đối tƣợng Pair. Sau đó chúng ta
sẽ tạo những đối tƣợng ủy quyền để đóng gói những phƣơng thức của chúng,
những phƣơng thức này phải phù hợp với ký pháp và kiểu trả về của ủy quyền.
Sau cùng chúng ta sẽ yêu cầu những đối tƣợng Pair này sắp xếp những đối
tƣợng Student và Cat, ta làm từng bƣớc nhƣ sau:
Bắt đầu bằng việc tạo phƣơng thức khởi dựng Pair lấy hai đối tƣợng và đƣa
chúng vào trong từng mảng riêng:
public class Pair
{
// đưa vào 2 đối tượng theo thứ tự
public Pair( object firstObjectr, object secondObject)
{
thePair[0] = firstObject
thePair[1] = secondObject;
}
// biến lưu giữ hai đối tượng
private object[] thePair = new object[2];
}
Tiếp theo là chúng ta phủ quyết phương thức ToString() để chứa giá trị mới
của hai đối tượng mà Pair nắm giữ:
public override string ToStringO
{
// xuất thứ tự đối tượng thứ nhất trước đối tượng thứ hai return
thePair[0].ToString() +"," + thePair[1].ToString();
}
Bây giờ thì chúng ta đã có hai đối tƣợng bên trong của Pair và chúng ta có
thể xuất giá trị của chúng ra màn hình. Tiếp tục là chúng ta sẽ thực hiện việc sắp
xếp và in kết quả sắp xếp. Hiện tại thì không xác định đƣợc loại đối tƣợng mà
chúng ta có, do đó chúng ta sẽ ủy quyền quyết định thứ tự sắp xếp cho chính bản
thân các đối tƣợng mà Pair lƣu giữ bên trong. Do vậy, chúng ta yêu cầu rằng
mỗi đối tƣợng đƣợc lƣu giữ bên trong Pair thực hiện việc kiểm tra xem đối
tƣợng nào sắp trƣớc. Phƣơng thức này lấy hai tham số đối tƣợng và trả về giá trị
kiểu liệt kê: theFirstComeFirst nếu đối tƣợng đầu tiên đƣợc đến trƣớc và
theSecondComeFirst nếu giá trị thứ hai đến trƣớc.
Những phƣơng thức yêu cầu sẽ đƣợc đóng gói bởi ủy quyền WhichIsFirst
đƣợc định nghĩa bên trong lớp Pair:
public delegate comparison WhichIsFirst( object objl, object obj2);
Giá trị trả về là kiểu comparison đây là kiểu liệt kê:
public enum comparison {
theFirstComesFirst = 1, theSecondComesFirst = 2
}
Bất cứ phƣơng thức tĩnh nào lấy hai tham số đối tƣợng object và trả về kiểu
comparison có thể đƣợc đóng gói bởi ủy quyền vào lúc thực thi.
Lúc này chúng ta định nghĩa phƣơng thức Sort cho lớp Pair:
public void Sort( WhichIsFirst theDelegateFunc)
{
if (theDelegateFunc(thePair[0], thePair[1]) = =
comparison.theSecondComeFirst)
{
object temp = thePair[0]; thePair[0] = thePair[1]; thePair[1] =
temp;
}
}
Phƣơng thức này lấy một tham số: một ủy quyền có kiểu WhichIsFirst với
tên là theDelegateFunc. Phƣơng thức Sort giao phó trách nhiệm quyết định thứ
tự đến trƣớc sau của hai đối tƣợng bên trong Pair đến phƣơng thức đƣợc đóng
gói bởi ủy quyền. Bên trong thân của Sort, phƣơng thức ủy quyền đƣợc gọi và
trả về một giá trị, giá trị này là một trong hai giá trị liệt kê của comparison.
Nếu giá trị trả về là theSecondComesFirst, đối tƣợng bên trong của Pair sẽ
đƣợc hoán đổi vị trí, trƣờng hợp ngƣợc lại thì không làm gì cả.
Hãy tƣởng tƣợng chúng ta đang sắp xếp những Student theo tên. Chúng ta
viết một phƣơng thức trả về theFirstComesFirst nếu tên của sinh viên đầu tiên
đến trƣớc và the- SecondComesFirst nếu tên của sinh viên thứ hai đến trƣớc.
Nếu chúng ta đƣa vào là “Amy, Beth” thì phƣơng thức trả về kết quả là
theFirstComesFirst. Và ngƣợc lại nếu chúng ta truyền “Beth, Amy” thì kết quả
trả về là theSecondComesFirst. Khi chúng ta nhận đƣợc kết quả
theSecondComesFirst, phƣơng thức Sort sẽ đảo hai đối tƣợng này trong mảng,
và thiết lập là Amy ở vị trí đầu còn Beth ở vị trí thứ hai.
Tiếp theo chúng ta sẽ thêm một phƣơng thức ReverseSort, phƣơng thức này
đặt các mục trong mảng theo thứ tự đảo ngƣợc lại:
public void ReverseSort( WhichIsFirst theDeleagteFunc)
{
if ( theDelegateFunc( thePair[0], thePair[1]) = =
comparison.theFirstComesFirst)
{
object temp = thePair[0]; thePair[0] = thePair[1]; thePair[1] =
temp;
}
}
Việc thực hiện cũng tƣơng tự nhƣ phƣơng thức Sort. Tuy nhiên, phƣơng thức
thực hiện việc hoán đổi nếu phƣơng thức ủy quyền xác định là đối tƣợng trƣớc
tới trƣớc. Do vậy, kết quả thực hiện của phƣơng thức là đối tƣợng thứ hai sẽ đến
trƣớc. Lúc này nếu chúng ta truyền vào là “Amy, Beth”, phƣơng thức ủy quyền
sẽ trả về theFirstComesFirst, và phƣơng thức ReverseSort sẽ hoán đổi vị trí của
hai đối tƣợng này, thiết lập Beth đến trƣớc. Điều này cho phép chúng ta sử dụng
cùng phƣơng thức ủy quyền tƣơng tự nhƣ Sort, mà không cần yêu cầu đối tƣợng
hỗ trợ phƣơng thức trả về giá trị đƣợc sắp ngƣợc.
Lúc này điều cần thiết là chúng ta tạo ra vài đối tƣợng để sắp xếp. Ta tạo hai
lớp đối tƣợng đơn giản nhƣ sau: lớp đối tƣợng Student và lớp đối tƣợng Cat.
Gán cho đối tƣợng Student một tên vào lúc tạo:
public class Student {
public Student (string name)
{
this.name = name;
}
}
Lớp đối tƣợng Student này yêu cầu hai phƣơng thức, một là phƣơng thức phủ
quyết ToString (), và một phƣơng thức khác đƣợc đóng gói nhƣ là phƣơng thức
ủy quyền.
Lớp Student phải phủ quyết phƣơng thức ToString() để cho phƣơng thức
ToString() của lớp Pair sử dụng một cách chính xác. Việc thực thi này thì không
có gì phức tạp mà chỉ đơn thuần là trả về tên của sinh viên:
ProcessImage()
{
for (int i = 0; i < numEffectsRegistered; i++)
{
arrayOfEffects[i]();
}
}
// biến thành viên
private DoEffect[] arrayOfEffects;
private Image image;
private int numEffectsRegistered = 0;
}
// lớp Test để kiểm chứng chương trình public class Test
{
Kết quả:
12/24/200 3:21:20 PM
Nhƣ chúng ta có thể thấy, kết quả chƣơng trình đƣợc thực thi vào lúc 3:21
vào ngày 24 tháng 12. Danh sách này thể hiện một đồng hồ xuất hiện ở dòng
lệnh, và chúng dƣờng nhƣ là đƣợc
cập nhật trong mỗi giây đồng hồ. Thật vậy, nó thông thƣờng đƣợc cập nhật
nhiều hơn một lần, do đó chúng ta lƣu ý là giây đồng hồ thay đổi chỉ khi giá trị
xuất hiện thật sự khác nhau. Chƣơng trình sẽ chạy mãi đến khi nào ta nhấn thoát
bằng Ctrl + C.
Trong chƣơng trình ta sử dụng kiểu dữ liệu DateTime, đây là một cấu trúc đƣợc
chứa trong namespace System bên trong thƣ viện cơ sở. Cấu trúc này có một
thuộc tính tĩnh là Now trả về thời gian hiện hành. Có nhiều dữ liệu thành viên và
những phƣơng thức đƣợc thêm vào trong cấu trúc DateTime. Chúng ta có thể
tìm hiểu thêm về DateTime trong thƣ viện trực tuyến về các lớp cơ sở của .NET
Framework.
Cách tốt nhất để hiện thị ngày giờ trên màn hình là sử dụng Timer. Một Timer
cho phép một xử lý (hình thức của một delegate) đƣợc gọi tại một thời gian xác
định hay sau một chu kỳ nào đó trôi qua. Framework chứa một lớp Timer bên
trong namespace System.Timers. Lớp này đƣợc sử dụng trong ví dụ 12.2 theo
sau:
Ví dụ 9.2: Sử dụng Timer.
// Timer02.cs: hiểu thị ngày giờ sử dụng Timer
// nhấn Ctrl+C hay 'q' và Enter để thoát
namespace Programming_CSharp
{
using System;
using System.Timers; public class Tester
{
public static void Main()
{
Timer myTimer = new Timer();
// khai báo hàm xử lý
myTimer.Elapsed += new ElapsedEventHandler( DisplayTimeEvent);
// khoảng thời gian delay myTimer.Interval = 1000; myTimer.Start();
// thực hiện vòng lặp để chờ thoát while ( Console.Read() != 'q')
{
; // không làm gì hết!
}
}
public static void DisplayTimeEvent( object source, ElapsedEventArgs
t)
{
Console.Write("\n{0}", DateTime.Now);
}
}
}
Kết quả:
15/9/2014 9:45:20 PM
Kết quả thực hiện cũng giống nhƣ ví dụ trƣớc. Tuy nhiên, chƣơng trình
này thực hiện tốt hơn nhiều so với chƣơng trình ban đầu. Thay vì cập nhật
không ngừng ngày giờ đƣợc hiển thị, chƣơng trình này chỉ cập nhật sau khoảng
1 giây. Chúng ta hãy xem kỹ cách mà Timer làm việc. Một đối tƣợng Timer mới
đƣợc tạo ra, thuộc tính Interval đƣợc thiết lập. Tiếp theo phƣơng thức sẽ đƣợc
thực hiện sau khoảng thời gian interval đƣợc gắn với Timer. Trong trƣờng hợp
này là phƣơng thức DisplayTimeEvent sẽ đựơc thực thi, phƣơng thức đƣợc định
nghĩa ở bên dƣới.
Khi Timer thực hiện phƣơng thức Start thì nó sẽ bắt đầu tính interval. Một
thuộc tính thành viên khác của Timer là AutoReset mà chúng ta cũng cần biết
là: nếu chúng ta thay đổi giá trị mặc định của nó từ true sang false, thì sự kiện
Timer chỉ thực hiện duy nhất một lần. Khi AutoReset có giá trị true hay ta thiết
lập giá trị true thì Timer sẽ kích hoạt sự kiện và thực thi phƣơng thức mỗi một
thời gian đƣợc đƣa ra (interval).
Trong chƣơng trình này vẫn chứa một vòng lặp thực hiện đến khi nào
ngƣời dùng nhấn ký tự „q‟ và Enter. Nếu không chƣơng trình thực hiện tiếp tục
vòng lặp. Không có gì thực hiện trong vòng lặp này, nếu muốn chúng ta có thể
thêm vào trong vòng lặp những xứ lý khác. Chúng ta cũng không cần thiết phải
gọi phƣơng thức DisplayTimeEvent trong vòng lặp bởi vì nó sẽ đƣợc gọi tự
động vào khoảng thời gian xác định interval.
Timer trong chƣơng trình này dùng để thể hiện ngày giờ trên màn hình.
Timer và những sự kiện của Timer cũng có thể đƣợc sử dụng cho nhiều chƣơng
trình khác. Nhƣ chúng ta có thể tạo Timer để tắt một chƣơng trình khác vào một
thời điểm đƣa ra. Chúng ta cũng có thể tạo chƣơng trình backup thƣờng xuyên
để sao chép những dữ liệu quan trọng theo một định kỳ thời gian nào đó. Hay
chúng ta cũng có thể tạo một chƣơng trình tự động log off một ngƣời sử dụng
hay kết thúc một chƣơng trình sau một khoảng thời gian mà không có bất cứ
hoạt động nào xảy ra. Nói chung là có rất nhiều cách sử dụng Timer này, và lớp
Timer này rất hữa ích.
9.3 Lớp về thƣ mục và hệ thống
Đôi khi chúng ta cần biết thông tin hệ thống của máy mà chƣơng trình
đang thực hiện, điều này không khó khăn gì, .NET hỗ trợ một số lớp cơ bản để
thực hiện việc này. Trong ví dụ minh họa 9.3 bên dƣới sẽ trình bày cách lấy các
thông tin về máy tính và môi trƣờng của nó. Việc thực hiện này thông qua sử
dụng lớp Environment, trong lớp này chứa một số dữ liệu thành viên tĩnh và
chúng ta sẽ thú vị với thông tin của chúng.
Ví dụ 9.3: Sử dụng lớp Environment.
// env01.cs: hiển thị thông tin của lớp Environment namespace
Programming_CSharp
{
Using System;
class Tester {
public static void Main()
{
// các thuộc tính
Console.WriteLine("**************************");
Console.WriteLine("Command: {0}", Environment.CommandLine);
Console.WriteLine("Curr Dir: {0}", Environment.CurrentDirectory);
Console.WriteLine("Sys Dir: {0}", Environment.SystemDirectory);
Console.WriteLine("Version: {0}", Environment.Version);
Console.WriteLine("OS Version: {0}", Environment.OSVersion);
Console.WriteLine("Machine: {0}", Environment.MachineName);
Console.WriteLine("Memory: {0}", Environment.WorkingSet);
// dùng một vài các phƣơng thức Console
WriteLine("**************************"); string [] args =
Environment.GetCommandLineArgs(); for( int i = 0; i < args.Length;
i+ + )
{
Console.WriteLine("Arg {0}: {1}", i, args[i]);
}
Console WriteLine("**************************"); string []
drivers = Environment.GetLogicalDrives(); for( int i = 0; i <
drivers.Length; i+ + )
{
Console.WriteLine("Drive {0}: {1}", i, drivers[i]);
}
Console WriteLine("**************************");
Console.WriteLine("Path: {0}",
Environment.GetEnvironmentVariable("Path"));
Console WriteLine("**************************");
}
}
}
Kết quả thực hiện với máy tính của tác giả (kết quả sẽ khác với máy tính của
bạn:)
**************************
Command: D:\Working\ConsoleApplication1\bin\Debug\Env01.exe Curr Dir:
D:\Working\ConsoleApplication1\bin\Debug Sys Dir: C:\WINDOWS\System32
Version: 1.0.3705.0
OS Version: Microsoft Windows NT 5.1.2600.0 Machine: MUN Memory: 4 57
52 32 **************************
Arg 0: D:\Working\ConsoleApplication1\bin\Debug\Env01.exe
**************************
Drive 0: A:\
Drive 1: C:\
Drive 2: D:\
Drive 3: E:\
**************************
Path:
C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\
WINDOWS;C:\WINDO
WS\COMMAND;C:\NC
Nhƣ chúng ta thấy thì những thành viên tĩnh của lớp Environment cung
cấp cho ta những thông tin hệ thống và môi trƣờng. Đầu tiên là lệnh thực hiện
đƣợc đƣa ra chính là chƣơng trình đang thực thi tức là chƣơng trình Env01.exe,
thuộc tính đƣợc dùng để lấy là Command- Line. Thƣ mục hiện hành chính là thƣ
mục chứa chƣơng trình thực thi thông qua thuộc tính CurrentDirectory. Tƣơng
tự nhƣ vậy các thuộc tính hệ thống nhƣ: thƣ mục hệ thống, phiên bản OS, tên
máy tính, bộ nhớ cũng đƣợc lấy ra.
Tiếp theo là hai phƣơng thức của lớp Environment trả về mảng chuỗi ký tự, bao
gồm phƣơng thức lấy đối mục dòng lệnh GetCommandLineArgs và phƣơng
thức nhận thông tin về ở đĩa logic trong máy tính GetLogicalDrives. Hai vòng
lặp đơn giản là để xuất giá trị từng thành phần trong mảng ra.Cuối cùng là
phƣơng thức GetEnvironmentVariable nhận biến môi trƣờng và những giá trị
của chúng trong hệ thống hiện thời.
9.4 Lớp Math
Từ đầu tới giờ chúng ta chỉ thực hiện các phép toán cơ bản nhƣ cộng, trừ,
nhân, chia, chia dƣ. Còn rất nhiều các phép toán mạnh hơn và cũng thƣờng sử
dụng mà chúng chƣa đƣợc đề cập tới. C# cung cấp một tập hợp các phép toán
toán học bên trong những lớp cơ sở. Chúng đƣợc chứa bên trong của namespace
System.Math. Bảng 9.1 sau liệt kê những hàm toán học.
Lớp Math là lớp sealed, do đó chúng ta không thể xây dựng một lớp mới mà kế
thừa từ lớp này đƣợc. Thêm vào đó tất cả những lớp và dữ liệu thành viên đều là
tĩnh, do vậy chúng ta không thể tạo một đối tƣợng có kiểu Math. Thay vào đó
chúng ta sẽ sử dụng những thành viên và phƣơng thức với tên lớp.
Lớp Math
Phương thức Mô tả
Abs Trả về trị tuyệt đối của một số
Ceiling Trả về giá trị nhỏ nhất hay bằng giá trị đƣa ra
Exp Trả về giá trị e với mũ đƣa ra
Floor Trả về giá trị lớn nhất hay bằng giá trị đƣa ra
IEEERemainder Trả về phần dƣ của phép chia hai hai số thực.
Phép chia này theo tiêu chuẩn của IEEE cho
phép toán dấu chấm động nhị phân.
Log Trả về giá trị logarit của giá trị đƣa ra
Log10 Trả về giá trị logarit cơ số 10 của số đƣa ra
Max Trả về số lớn trong hai số
Min Trả về số nhỏ trong hai số
Pow Trả về kết quả xy
Round Trả về giá trị đƣợc làm tròn
Sign Trả về giá trị dấu của một số. -1 nếu số âm và 1
nếu số dƣơng
Sqrt Trả về căn bậc hai của một số
Acos Trả về giá trị một góc mà cosin bằng với giá trị
Asin Trả về giá trị một góc mà sin bằng với giá trị
đƣa ra
Atan Trả
đƣa về
ra giá trị của một góc mà tang bằng với góc
đƣa ra
Atan2 Trả về giá trị của một góc mà tang bằng với
tang của điểm (x,y) đƣa ra
Cos Trả về giá trị cosin của một góc đƣa ra
Cosh Trả về giá trị hyperbolic cosin của góc đƣa ra
Sin Trả về giá trị sin của góc đƣa ra
Phƣơng thức Mô tả
Nhiều ngƣời nhầm lẫn về sự khác nhau giữa tập tin và luồng. Một luồng
đơn giản là luồng của thông tin, chứa thông tin sẽ đƣợc chuyển qua, còn tập tin
thì để lƣu trữ thông tin.
Một luồng đƣợc sử dụng để gởi và nhận thông tin từ bộ nhớ, từ mạng, web, từ
một chuỗi,...Một luồng còn đƣợc sử dụng để đi vào và ra với một tập tin dữ liệu.
Thứ tự của việc đọc một tập tin
Khi đọc hay viết một tập tin, cần thiết phải theo một trình tự xác định. Đầu
tiên là phải thực hiện công việc mở tập tin. Nếu nhƣ tạo mới tập tin, thì việc mở
tập tin cùng lúc với việc tạo ra tập tin đó. Khi một tập tin đã mở, cần thiết phải
tạo cho nó một luồng để đặt thông tin vào trong một tập tin hay là lấy thông tin
ra từ tập tin. Khi tạo một luồng, cần thiết phải chỉ ra thông tin trực tiếp sẽ đƣợc
đi qua luồng. Sau khi tạo một luồng gắn với một tập tin, thì lúc này chúng ta có
thể thực hiện việc đọc ghi các dữ liệu trên tập tin. Khi thực hiện việc đọc thông
tin từ một tập tin, chúng ta cần thiết phải kiểm tra xem con trỏ tập tin đã chỉ tới
cuối tập tin chƣa, tức là chúng ta đã đọc đến cuối tập tin hay chƣa. Khi hoàn
thành việc đọc ghi thông tin trên tập tin thì tập tin cần phải đƣợc đóng lại.
Tóm lại các bƣớc cơ bản để làm việc với một tậo tin là:
• Bƣớc 1: Mở hay tạo mới tập tin
• Bƣớc 2: Thiết lập một luồng ghi hay đọc từ tập tin
• Bƣớc 3: Đọc hay ghi dữ liệu lên tập tin
• Bƣớc 4: Đóng lập tin lại
Các phương thức cho việc tạo và mở tập tin
Có nhiều kiểu luồng khác nhau. Chúng ta sẽ sử dụng những luồng khác nhau
và những phƣơng thức khác nhau phụ thuộc vào kiểu dữ liệu bên trong của tập
tin. Trong phần này, việc đọc/ghi sẽ đƣợc thực hiện trên tập tin văn bản. Trong
phần kế tiếp chúng ta học cách đọc và viết thông tin trên tập tin nhị phân. Thông
tin nhị phân bao hàm khả năng mạnh lƣu trữ giá trị số và bất cứ kiểu dữ liệu nào
khác.
Để mở một tập tin trên đĩa cho việc đọc và viết tập tin văn bản, chúng ta cần
phải sử dụng cả hai lớp File và FileInfo. Một vài phƣơng thức có thể sử dụng
trong những lớp này. Các phƣơng thức này bao gồm:
AppendTex Mở một tập tin để và tập tin này có thể đƣợc thêm văn bản vào
t Tạo luồng StreamWriter sử dụng để thêm vào trong văn bản.
trong nó.
Create Tạo mới một tập tin
CreateText Tạo và mở một tập tin văn bản. Tạo ra một luồng
Open Mở một tập tin để đọc/viết. Mở một FileStream
StreamWriter.
OpenRead Mở một tập tin để đọc
OpenText Mở một tập tin văn bản để đọc. Tạo ra StreamReader để sử
OpenWrite Mở một tập tin cho việc đọc và ghi.
dụng.
Làm thế nào để chúng ta có thể biết đƣợc khi nào sử dụng lớp File chính xác
hơn là sử dụng lớp FileInfo nếu chúng cùng chứa những phƣơng thức tƣơng tự
với nhau. Thật ra hai lớp này có nhiều sự khác biệt. Lớp File chứa tất cả các
phƣơng thức tĩnh, thêm vào đó lớp File tự động kiểm tra permission trên một tập
tin. Trong khi đó nếu muốn dùng lớp FileInfo thì phải tạo thể hiện của lớp này.
Nếu muốn mở một tập tin chỉ một lần thì tốt nhất là sử dụng lớp File, còn nếu
chúng ta tổ chức việc sử dụng tập tin nhiều lần bên trong chƣơng trình, tốt nhất
là ta dùng lớp FileInfo. Hoặc nếu không chắc chắn cách sử dụng thì chúng ta có
thể sử dụng lớp FileInfo.
Viết vào một tập tin văn bản
Cách tốt nhất để nắm vững cách thức làm việc với tập tin là chúng ta sẽ bắt
tay vào tìm hiểu chƣơng trình. Trong phần này chúng ta sẽ tìm hiểu một ví dụ
minh họa việc tạo ra một tập tin văn bản rồi sau đó viết lại thông tin vào nó.
Ví dụ 9.6: Viết dữ liệu vào tập tin văn bản.
//writing.cs:: viết vào một tập tin văn bản
namespace Programming_Csharp
{
using System; using System.IO;
public classTester
{
public static void Main(String[] args)
{
if (args.Length < 1)
{
Console.WriteLine("Phai nhap ten tap tin.");
}
else
{
StreamWriter myFile = File.CreateText( args[0]);
myFile.WriteLine("Khong co viec gi kho");
myFile.WriteLine("Chi so long khong ben");
myFile.WriteLine("Dao nui va lap bien");
myFile.WriteLine("Quyet chi at lam nen"); for(int i=0; i < 10;
i++)
{
myFile.Write("{0} ",i);
}
myFile.Close();
}
}
}
}
Khi chạy chƣơng trình trên chúng ta phải cung cấp tên của tập tin đƣợc
tạo mới, nếu không cung cấp thì chƣơng trình sẽ không thực hiện việc tạo tập
tin. Giả sử chúng ta có cung cấp một tham số dòng lệnh cho chƣơng trình thì
một tập tin văn bản mới đƣợc tạo ra có nội dung nhƣ sau:
Khong co viec gi kho
Chi so long khong ben
Dao nui va lap bien
Quyet chi at lam nen
0123456789
Trong chƣơng trình trên không thực hiện việc xử lý ngoại lệ. Điều này dẫn đến
chƣơng trình có thể phát sinh ra những ngoại lệ và những ngoại lệ này không
đƣợc xử lý. Đây là cách lập trình không tốt, nên yêu cầu ngƣời đọc nên thêm các
xử lý ngoại lệ vào chƣơng trình trên, ngoại lệ này cũng tƣơng tự nhƣ ngoại lệ
trong ví dụ trƣớc.
Nhƣ chúng ta thấy hàm Main có tham số và tham số này sẽ đƣợc nhận thông
qua dòng lệnh, trong các ví dụ trƣớc, chƣơng trình nhận tham số dòng lệnh
thông qua lớp Environment, còn ở đây ta khai báo tham số dòng lệnh trực tiếp
cho chƣơng trình. Dòng lệnh đầu tiên của hàm Main() là kiểm tra số tham số
nhập vào, nếu không có tham số nào chƣơng trình sẽ xuất ra thông báo là không
có tên tập tin và kết thúc chƣơng trình.
Trong trƣờng hợp cung cấp tham số đầy đủ chƣơng trình sẽ thực hiện việc tạo
mới tập tin. Phƣơng thức CreateText của lớp File đƣợc gọi để tạo ra một đối
tƣợng StreamWriter mới gọi là myFile. Tham số đƣợc truyền cho hàm là tên của
tập tin sẽ đƣợc tạo. Kết quả cuối cùng của dòng lệnh này là tạo ra một tập tin
văn bản mới. Dữ liệu s4 đƣợc đƣa vào tập tin thông qua StreamWriter với thể
của nó là myFile.
Ghi chú:Nếu một tập tin hiện hữu với cùng tên nhƣ tập tin mà chúng ta tạo ra
thì tập tin cũ sẽ đƣợc viết chồng, tức là dữ liệu bên trong tập tin cũ sẽ bị xóa
mất.
Hình sau minh họa việc thực hiện tạo tập tin và đƣa dữ liệu vào
Disk file
arg[0]
lblHoTen
btn_Ho
txtHo txtTen
- Quy định form hiễn thị giữa màn hình. Không cho ngƣời sử dụng thay đổi
kích thƣớc form.
- Quy định việc di chuyển Tab hợp lý.
- Các ListBox đƣợc phép chọn nhiều mục (kết hợp giữa phím Shift, Control
và chuột)
- Khi ngƣời sử dụng Click nút “Cập nhật” hoặc nhấn phím Enter thì mặt định
nhập tên sinh viên từ textbox vào danh sách lớp A (không chấp nhận dữ liệu
rỗng). Theo đó ngƣời sử dụng có thể chọn lớp để cập nhật từ combobox lớp.
- Các nút “>” và “<” khi đƣợc Click sẽ di chuyển tất cả các mục đang chọn
sang ListBox bên kia tƣơng ứng.
- Các nút “>>” và “<<” khi đƣợc Click sẽ di chuyển toàn các mục sang
Listbox bên kia tƣơng ứng.
- Nút lệnh “Xóa lớp A”, “Xóa lớp B” cho phép xóa các mục đang chọn trong
list hiện hành.
Bài 5:
Thiết kế giao diện nhƣ hình vẽ
Yêu cầu:
Khi form thực thi
Tất cả text boxes và labels đều rỗng.
Đặt focus vào text box Tên Hàng và xác lập việc di chuyển Tab hợp lý
(menu View\Tab Order)
Form hiễn thị giữa màn hình. (StartPosition =CenterScreen )
Thiết lập nút Thanh Tóan nhận sự kiện phím Enter (AcceptButton), nút Tiếp
nhận sự kiện phím ESC (CancelButton)
Thực hiện canh lề phải cho các đối tƣợng chứa dữ liệu là số (Text
Align=Right)
Xử lý nút lệnh “Thanh Tóan”
Các text box Số lƣợng, Đơn Giá buộc phải có giá trị và chỉ cho phép nhập số.
Thêm vào đó:
Số lƣợng >=0. (int)
Đơn giá >=0 (decimal)
Cung cấp các Message Box khác nhau để thông báo rõ ràng cho việc dữ liệu bị
nhập sai.
Tính toán cho mặt hàng hiện tại xuất kết quả ra label Thành Tiền.
Thành tiền = số lƣợng * đơn giá;
Nút Tiếp:
Xóa nội dung các textbox và label
Đặt focus vào textbox đầu tiên
Nút Kết Thúc:
Phát sinh Messagebox hỏi lại việc đóng form có chính xác không, nếu ngƣời sử
dụng xác nhận bằng nút Yes thì cho thóat chƣơng trình
Bài 7:
Mục đích:
Sử dụng hằng số, biến toàn cục (module variable)
Yêu cầu
Thiết kế giao diện nhƣ hình bên dƣới.
Yêu cầu:
1) Sinh viên tự thiết kế các đối tƣợng trên form cho phù hợp
2) Thiết đặt TabIndex cho hợp lý.
3) Nhấn nút Tính hoặc Enter thì tính và hiện thông tin lên các ô tƣơng ứng.
4) Nhấn vào Exit hoặc Esc thì thoát chƣơng trình.
Ghi chú: sử dụng biến toàn cục và hằng số cho hợp lý
Bài 8:
Mục đích:
Sử dụng hằng số, biến toàn cục (module variable)
Yêu cầu
Thiết kế giao diện nhƣ hình bên dƣới.
1. Khi chƣơng trình vừa thực thi:
- Form đƣợc hiển thị giữa màn hình. Canh lề phải cho các control chứa dữ liệu
là số, thiết lập hệ thống Tab hợp lý.
- Thiết lập nút “Tính TT” nhận phím Enter thay cho sự kiện Click.
2. Yêu cầu cài đặt
Tên KHách hàng không đƣợc phép rỗng.
Số lƣợng sách là số nguyên dƣơng
Mỗi quyển sách đều đƣợc bán với giá là 20000.
Nút “Tính TT”: tính thành tiền và xuất kết quả lên lable Thành tiền
Thành tiền = Số lƣợng * Đơn Giá ;
Nút “Tiếp”:
Xóa nội dung các control trong gourpbox “Hóa Đơn”
Đặt focus cho Textbox “Tên Khách Hàng”
Nút “Thống Kê”: Tính và hiển thị kết quả trên các lable trong groupbox
“Thống kê”
Nút “Kết Thúc”: Phát sinh messageBox hỏi lại ngƣời dùng có thật sự muốn
đóng ứng dụng hay không?
Bài 9:
Mục đích:
Sử dụng cấu trúc điều khiển dạng lựa chọn
Yêu cầu
Thiết kế giao diện nhƣ hình bên dƣới.
Nút “Thực hiện đổi”: sẽ đổi năm dƣơng lịch sang năm âm lịch tƣơng
ứng.
Bài 10:
Mục đích:
Sử dụng mảng một chiều
Yêu cầu:
Thiết kế Form nhƣ hình sau:
Trong đó 2 group box sẽ chứa 2 label, để hiễn thị mảng và kết quả xử lý trên
các button tƣơng ứng.
Hƣớng dẫn :
1. khai báo mảng tòan cục, với kích thƣớc mảng MAX_SIZE là 1 hằng số.
Yêu cầu:
Thiế kế class Hóa Đơn đáp ứng yêu cầu xử lý của giao diện trên.
Hƣớng dẫn:
1. Tạo các field private:
Tên hàng, số lượng, đon giá.
2. Tạo các field static:
Số hóa đon, doanh thu.
3. Tạo các property thông thƣờng.
4. Tạo property ReadOnly đối với các Field static.
5. Tạo các Contructor
6. Tạo các phƣơng thức
Bài 12:
Yêu cầu:
1. Thiết kế Form nhƣ hình sau:
2. Thiết kế class Hóa Đơn đáp ứng yêu cầu xử lý của giao diện trên.
Hƣớng dẫn:
Tạo các field private:
Họ tên, địa chỉ, điện thoại, giá tiền, màu xe.
Tạo các field static:
Số khách hàng, số khách mua xe đen, doanh thu.
Tạo các property thông thƣờng.
Tạo property ReadOnly đối với các Field static.
Tạo các Contructor
Ví dụ: