Đề cương lập trình đối tượng

Đề cương lập trình đối tượng

Đ
ơng Lập tnh ớng đối ợng với C#
Trang 1
MỤC LC
MỤC LỤC ..................................................................................................... 1
Bài 1: Các khái niệm cơ bản về lập trình hướng đối tượng ....................... 4
1.1. Lịch sử phát triển của phương pháp lập tnh .................................................................................... 4
1.1.1 Lập trình không cấu trúc (hay lập trình tuyến tính) ................................................................. 4
1.1.2 Lập trình hướng chức năng(lập trình cấu trúc) ............................................................................ 5
1.1.3 Lập trình module ........................................................................................................................ 6
1.1.4 Lập trình hướng đối tượng .......................................................................................................... 8
1.2 Một số khái niệm trong lập trình hướng đối ng ............................................................................. 9
1.2.1 Trừu tượng hóa dữ liệu (Data Abstraction).................................................................................. 9
1.2.2 Lớp (Class) ............................................................................................................................... 12
1.2.3 Đối tượng (Object).................................................................................................................... 14
1.2.4 Thuộc nh ................................................................................................................................ 16
1.2.5 Hoạt động (Operation) .............................................................................................................. 17
1.2.6 Phương thức (Method) .............................................................................................................. 17
1.2.7 Thông điệp (Message)............................................................................................................... 18
1.2.8 Sự kiện (Event) ......................................................................................................................... 19
1.2.8 Phân biệt giữa lớp đối tượng ................................................................................................ 19
1.2.9 Thiết lập (Construction) Hủy (Destruction) .......................................................................... 19
1.2.10 Tính Bền vững (Persistence) ................................................................................................... 21
1.2.11 Tính Đóng gói dữ liệu ............................................................................................................. 22
1.2.12 Tính thừa kế ........................................................................................................................... 23
1.2.13 Tính Đa Thừa kế..................................................................................................................... 25
1.2.14 Tính Đa nh........................................................................................................................... 26
1.3 Các ưu điểm của lập trình hướng đối tượng ..................................................................................... 28
1.3.1. c ưu điểm của lập trình hướng đối tượng ............................................................................. 28
1.3.2. Mốt số nn ngữ lập trình hướng đối tượng ............................................................................. 28
1.3.3. Ngôn ngữ lập trình C# ............................................................................................................. 30
Bài 2: Lớpđi tượng(1) ......................................................................... 32
2.1. Lớp ................................................................................................................................................. 32
2.1.1. Định nghĩa lớp ......................................................................................................................... 32
2.1.2 Thành phần dữ liệu ................................................................................................................... 36
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 2
2.1.3 Phương thức ............................................................................................................................. 38
2.1.4. c từ khoá đặc tả truy cập ...................................................................................................... 38
2.1.5. Khai báo đối tượng, mảng đối ợng ........................................................................................ 41
2.1.6. Định nga chồng phương thức ................................................................................................ 42
2.1.7. Thuộc tính thủ tục thuộc tính .............................................................................................. 44
2.1.8. Từ khoá this ............................................................................................................................. 48
Bài 3: Lớpđi tượng(2) ......................................................................... 52
3.1. Phép n các đối tượng .................................................................................................................. 52
3.2. Phương thức thiết lập sao cp................................................................................................... 52
3.2.1. Phương thức thiết lập .............................................................................................................. 52
3.2.2. Phương thức sao chép .............................................................................................................. 59
3.3. Hu bỏ đối tượng, chế gom rác trong .Net ................................................................................ 60
Bài 4: Lớpđi tượng(3) ......................................................................... 64
4.1. Thành phần tĩnh cách sử dụng .................................................................................................... 64
4.1.1. Thành phần dữ liệu tĩnh. .......................................................................................................... 64
4.1.2. Phương thức nh ..................................................................................................................... 66
4.2. Lớp bao cách sử dụng ................................................................................................................ 69
Bài 5: Kế thừa đa hình(1) ...................................................................... 71
5.1. Giới thiệu chung về thừa kế ............................................................................................................ 71
5.2. Xây dựng lớp dẫn xuất thừa kế từ lớp sở ................................................................................... 74
5.3. Mức truy cập trong lớp dẫn xuất ..................................................................................................... 77
5.4. Gọi phương thức khởi tạo của lớp sở ......................................................................................... 78
5.5. Truy xuất các thành phần của lớp sở .......................................................................................... 79
5.6. Boxing Unboxing dữ liệu ........................................................................................................... 80
5.7. Các lớp lồng nhau ........................................................................................................................... 82
Bài 6: Kế thừa đa hình(2) ...................................................................... 85
6.1. Giới thiệu chung về đa nh ............................................................................................................ 85
6.2. Phương thức đa nh....................................................................................................................... 85
6.3. Từ khoá new override ................................................................................................................ 89
Bài 7: Kế thừa đa hình(3) ...................................................................... 92
7.1 Lớp trừu tượng ................................................................................................................................ 92
7.1.1. Xây dựng lớp sở trừu ợng ............................................................................................... 92
7.1.2. Kế thừa từ lớp trừu ợng ....................................................................................................... 95
7.1.3. Hn chế của lớp trừu ng ..................................................................................................... 99
7.2. Lớp lập (sealed class) ............................................................................................................... 100
7.3. Lớp Object ................................................................................................................................... 101
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 3
Bài 8: Giao diện ........................................................................................ 104
8.1. Giao diện ...................................................................................................................................... 104
8.1.1. Xây dựng giao din .................................................................................................................104
8.1.2. Khai báo thuộc tính của giao din ......................................................................................... 105
8.1.3. Khai báo phương thức của giao diện ...................................................................................... 106
8.2. Thực thi giao diện......................................................................................................................... 107
8.2.1. Thực thi giao diện ...................................................................................................................107
8.2.2. Toán tử is .............................................................................................................................. 111
8.2.3. Toán tử as .............................................................................................................................. 113
8.3. Thực thi giao diện......................................................................................................................... 114
8.3.1. Thực thi giao diện ...................................................................................................................114
8.3.2. Mở rộng giao din ................................................................................................................ 124
8.3.3. Tham chiếu đối tượng của lớp con qua giao diện ................................................................... 125
8.3.4. Giao diện đối lập với lớp trừu tượng .......................................................................................126
8.4 Kỹ thuật xử ngoại lệ .................................................................................................................. 127
8.4.1 Khái niệm xử ngoi lệ ........................................................................................................ 127
8.4.2. c lớp ngoại lệ của .Net ........................................................................................................133
8.4.3 Ném ngoại lệ ......................................................................................................................... 134
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 4
Bài 1: Các khái nim bn về lp trình hưng đi tưng
1.1. Lch sử phát trin ca phương pháp lập trình
Phần này trình bày về mt số kỹ thuật hay phương pháp lập tnh được phát trin để giải
quyết các vấn đề trong Tin hc kể t khi máy tính ra đời. Sự phát trin của các kỹ thuật
lập trình liên quan chặt chẽ tới sự phát trin phn cứng của máy vi tính cũng như việc
ứng dụng máy tính vào giải quyết các vấn đề trong thực tế. Chúng ta thể chia c
phương pháp lập trình thành các kiểu sau:
Lập trình không cấu trúc
Lập trình hướng thủ tục
Lập trình theo kiểu module hóa
Lập trình hướng đối tượng
Chúng ta sẽ lần lượt xem xét các kỹ thuật lập trình này.
1.1.1 Lập trình không cấu trúc (hay lập trình tuyến tính)
Thông thường mi người bắt đầu học lập trình bằng cách viết các chương trình nhỏ
đơn giản chỉ chứa mt chương trình chính”. đây mt chương trình chính nghĩa là
mt tập các lnh hoặc câu lnh làm việc vi các dữ liệu toàn cục trong cả chương trình
(các biến ng trong chương trình các biến toàn cục). Chúng ta thể minh hobng
hình vẽ sau đây:
Lập trình không cấu trúc. Chương trình cnh thao tác
trực tiếp trên các dữ liệu tn cục
Một số nhược đim của lập trình không cấu trúc:
+ Lập trình không cấu trúc không khả năng kiểm soát tính thy được của dữ
liệu. Mọi dữ liệu trong chương trình đều là biến toàn cục do đó có thể bị thay đổi
bởi bất kỳ phần nào đó của chương trình.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 5
+ Việc không kiểm soát được tính thấy được của dữ liệu dẫn đến các khó khăn
trong việc gỡ li chương trình, đặc biệt là các chương trình lớn.
+ Kỹ thuật lập trình không cấu trúc rất nhiu bất lợi lớn khi chương trình đủ
lớn. dụ nếu chúng ta cần thực hin li mt đoạn câu lnh trên mt tập dữ liệu
khác t buộc phải copy đoạn lnh đó tới vị trí trong chương trình mà chúng ta
muốn thực hiện. Điều này làm nảy sinh ý tưởng trích ra các đoạn lệnh thường
xuyên cần thực hiện đó, đặt tên cho chúng đưa ra mt kỹ thuật cho phép gọi
trả về các giá trị từ các thủ tục này.
1.1.2 Lập trình hướng chức năng(lập trình cấu trúc)
Những ngôn ngữ lập trình truyền thống như: Pascal, C, Foxpro.. được gọi chung
ngôn ngữ lập trình hướng thủ tục. Theo ch tiếp cận hướng thủ tục t h thống phần
mềm được xem như y các ng việc cần thực hiện như đọc dữ liệu, tính toán, xử lý,
lập o cáo in ấn kết quả...Mỗi công việc đó sẽ dược thực hiện bởi mt hàm hay thủ
tục nhất định. Như vậy trọng tâm của cách tiếp cận y các hàm chức năng. i hơn
theo ch tiếp cận này, chương tnh được tổ chức thành các chương trình con. Mi
chương trình con đảm nhận xử một công việc nh trong toàn bộ hệ thống. Mỗi chương
tnh con này lại thể chia nhỏ thành các chương trình con nhỏ n. Quá trình phân chia
như vậy tiếp tục din ra cho đến khi các chương trình con nhận được đủ đơn giản. Người
ta gọi đó là quá trình làm mịn dần.
Khi tập trung vào trọng tâm phát trin các hàm tchúng ta li ít chú ý đến dữ liệu
( Nghĩa lập trình hướng thủ tục thì dữ liệu không được coi trọng) nhng cái các
hàm sử dụng để thực hin công việc của mình.
Cái gì sẽ xẩy ra đối với dữ liệu gắn dữ liệu vi các hàm như thế nào? Trong
chương trình nhiều hàm, thường t nhiều thành phần dữ liệu quan trọng sẽ được
khai báo tổng thể để cho nhiều hàm thể truy nhập, đọc làm thay đổi giá trị của biến
tổng thể. Ngoài ra mi hàm thể có các vùng dữ liệu riêng.
Để liên kết giữa các m thành mt đơn vị chương trình thông qua biến toàn cục.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 6
Điều này nghĩa nhiều hàm thể truy nhập, sử dụng dữ liệu chung , làm thay
đổi gtrị của chúng vậy rất khó kim soát. Nhất là đối với các chương trình lớn,
phức tạp t vấn đề càng trở nên khó khăn hơn.
Điều sẽ xẩy ra nếu như chúng ta muốn thay đi, b xung cấu trúc dữ liệu của
biến toàn cục. Đó là phải viết lại hầu như toàn bộ các hàm liên quan...
m li những đặc tính chính của phương pháp lập trình hướng thủ tục là:
+ Tập chung o công việc cần thực hiện (thuật toán)
+ Chương trình lớn được chia thành các hàm nhỏ hơn
+ Phần lớn các hàm sử dụng dữ liệu chung
+ Dữ liêu trong hệ thống được chuyển động từ m này sang hàm khác.
+ Hàm biến đổi dữ liệu từ dạng này sang dạng khác
+ Sử dụng cách tiếp cận top-down trong thiết kế chương trình
1.1.3 Lập trình module
Trong lập trình module các th tục cùng một chức năng chung sẽ được nhóm li
với nhau tạo thành một module riêng biệt. Một chương trình skhông chỉ bao gồm mt
phần đơn lẻ. được chia thành mt vài phần nhỏ hơn tương tác với nhau qua các lời
gọi thủ tục và tạo thành toàn b chương trình.
Dữ liệu chung
Dữ liệu chung
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 7
Lập trình module. Chương trình cnh sự kết hợp giữa các lời
gọi tới các thủ tục trong các module riêng biệt với c dữ liệu
thích hợp
Mỗi module dữ liệu riêng của nó. Điều này cho phép các module thể kiểm soát các
dữ liệu riêng của nó bằng các li gọi tới các thủ tục trong module đó. Tuy nhiên mi
module chỉ xuất hiện nhiều nhất mt ln trong cả chương tnh.
Yếu điểm của lập tnh th tục lập trình module hóa:
+ Khi độ phức tạp của chương trình tăng lên sự phthuộc của vào các kiểu dữ liu
bản mà nó xử ng tăng theo. Vấn đề trở nên ràng rằng cấu trúc dữ liệu s
dụng trong chương trình cũng quan trọng không kém các phép toán thực hin trên
chúng. Điều này càng lộ khi ch thước chương trình tăng. Các kiểu dữ liệu được
xử nhiều trong các thủ tục của một chương trình cấu trúc. Do đó khi thay đổi i
đặt của một kiểu dữ liệu sẽ dẫn đến nhiều thay đổi trong các thủ tục sử dụng nó.
+ Một nhược điểm nữa khi cần dùng nhiều nhóm làm việc để xây dựng một chương
tnh chung. Trong lập trình cấu trúc mỗi người sẽ được giao xây dựng mt s th
tục kiểu dữ liệu. Những lập trình viên xử các thủ tục khác nhau nhưng li liên
quan tới c kiểu dữ liệu dùng chung nên nếu một người thay đổi kiểu dữ liu thì sẽ
làm nh hưởng tới công việc của nhiều người khác, đặc biệt khi sai sót trong
việc liên lạc giữa các thành viên của nhóm.
+ Việc phát trin các phầm mềm mất nhiều thời gian tập trung xây dựng lại các cấu
trúc dữ liệu bản. Khi xây dựng một chương trình mới trong lập trình cấu trúc
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 8
lập trình viên thường phải xây dựng lại các cấu trúc dữ liệu bn cho phù hợp với
bài toán và điều này đôi khi rất mất thời gian.
1.1.4 Lập trình hướng đối ợng
Phong cách của phương pháp lập trình hướng đối tượng (OOP - Object Oriented
Programming) là tập chung vào dữ liệu. Theo cách tiếp cận này thì mt câu hỏi thường
được đặt ra là dữ liệu nào t được các phương thức(hay các hàm) nào xlý và người ta
đem i tất cả dữ liệu c phương thức liên quan với nhau thành một nhóm ging
như một l thuốc bao giờ cũng hai thứ: Các viên thuốc(dữ liệu) tgiấy trên đó ghi
cách dùng các viên thuốc đó(phương thức). Hai thứ đó gộp li thành mt kiểu dữ liệu gọi
dữ liệu đối tượng(Object). Lập trình sử dụng kiểu dữ liệu loi này gọi lập trình
hướng đối tượng.
Lập trình hướng đối tượng đặt trọng tâm vào đối tượng. Yếu tố quan trong quá
tnh phát trin chương trình không cho phép dữ liệu biến động tdo trong hệ thống.
Trong mt đối tương dữ liệu gắn chặt với các hàm thành viên thành các vùng riêng
chỉ các hàm đó tác động nên và cấm các hàm n ngoài truy nhập tới tuỳ tin.
Lập trình hướng đối tượng cho phép phân ch i toán tnh các thực thể được
gọi đối tượng và sau đó xây dựng các dữ liệu cùng các hàm xung quanh các đối tượng
đó.
Các đối tựng có thể tác động, trao đổi thông tin với nhau thông qua cơ chế thông
o(mesage) thông qua các phương thưc(hàm).
Lập trình hướng đối tượng các đặc nh sau:
Đối ợng A
Dữ liệu
c phương thức
Đối ợng B
Dữ liệu
c phương thức
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 9
+ Tập chung o dữ liệu thay cho các hàm
+ Chương trình được chia thành các đối tượng
+ Cấu trúc dữ liệu được thiết kế sao cho đặc tả được đối tượng gắn vi cấu trúc
dữ liu đó.
+ Dữ liệu được đóng i li, được che giấu và không cho phép các hàm ngoại lai
truy nhập tự do.
+ Các đối tượng tác động trao đổi thông tin với nhau qua các hàm.
+ thể dẽ dàng bổ sung dữ liệu các hàm mới vào đối tượng nào đó khi cần
thiết.
+ Chương trình được thiết kế theo cách tiếp cận từ dưới lên(bottom- up).
1.2 Một số khái nim trong lập trình hưng đi tưng
1.2.1 Trừu tượng hóa dữ liệu (Data Abstraction)
Khi mt lập trình viên phải phát triển mt chương trình ứng dụng t không
nghĩa là người ấy lập tức viết mã cho ứng dụng ấy. Trước hết, người y phải nghiên cứu
ứng dụng xác định những thành phn tạo nên ứng dụng. Kế tiếp, người ấy phải xác
định nhng thông tin cần thiết về mi thành phần.
Hãy khảo sát chương trình ng dụng cho việc mua bán xe hơi nói trên. Chương trình phi
xuất hóa đơn cho những xe hơi đã bán cho khách hàng. Để xuất một hóa đơn, chúng ta
cần những thông tin chi tiết về khách hàng. Vậy bước thứ nhất là xác định những đặc tính
của khách hàng.
Một vài đặc tính gắn kết với khách hàng :
n.
Địa chỉ.
Tuổi.
Chiều cao.
Màu tóc.
Từ danh ch kể trên, chúng ta xác định những đặc tính thiết yếu đối với ứng dụng. Bởi
chúng ta đang đề cập đến nhng khách hàng mua xe, thế nhng chi tiết thiết yếu là:
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 10
n.
Địa chỉ.
Còn nhng chi tiết khác (chiều cao, màu c …) là không quan trọng đối vi ng dụng.
Tuy nhiên, nếu chúng ta phát triển mt ứng dụng h trợ cho việc điều tra tội phạm t
những thông tin chẳng hn như màu tóc là thiết yếu.
Bên cạnh những chi tiết về khách hàng, nhng thông tin sau cũng cần thiết:
Kiểu xe được bán.
Nhân viên nào bán xe.
Bên cạnh những đặc tính của khách hàng, xe i nhân viên bán hàng, chúng ta cũng
cần liệt kê nhng hành động được thực hiện.
Công việc xuất hóa đơn đòi hỏi những hành động sau:
Nhập tên của khách hàng.
Nhập địa chỉ của khách hàng.
Nhập kiểu xe.
Nhập tên của nhân viên bán xe.
Xuất hóa đơn với định dạng đòi hi.
Khung thông tin bên dưới cho thấy những thuộc tính những hành động liên quan đến
mt hóa đơn:
Các thuộc tính
Tên của kch ng
Địa chỉ của khách hàng
Kiểu xe bán
Nhân viên bán xe
Các hành động
Nhập tên
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 11
Nhập địa ch
Nhập kiểu xe
Nhập tên nhân viên bán xe
Xuất hóa đơn
Định nghĩa
Tiếp theo, chúng ta muốn ứng dụng tính toán tiền hoa hồng cho nhân viên bán hàng.
Những thuộc tính liên kết với nhân viên bán hàng có tương quan vớing dụng này là:
n.
Số lượng xe n đưc.
Tiền hoa hồng.
Những hành động đòi buộc đối với công việc này :
Nhập tên nhân viên bán xe.
Nhập số lượng xe bán được.
Tính tiền hoa hồng kiếm được.
Những thuộc nh
n
Số lượng xe bán được
Tiền hoa hng
Những hành động
Nhập tên
Sự trừu tượng hóa dữ liệu là quá trình xác định nhóm các thuộc tính và các hành
động liên quan đến mt thực thể cụ thể, xét trong mi tương quan với ng dụng đang
phát trin.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 12
Nhập số lượng xe bán được
Tính tin hoa hồng
Như thế, việc trừu tượng hóa dữ liệu tra đặt ra câu hi ‘Đâu là những thuộc tính và những
hành động cần thiết cho mt vấn đề đặt ra?’
Những ưu điểm của việc Trừu tượng hóa:
Tập trung o vấn đề.
Xác định những đặc tính thiết yếu những nh động cần thiết.
Giảm thiểu những chi tiết không cần thiết.
Việc trừu tượng hóa dữ liệu là cần thiết, bởi không thể phỏng tt cả các hành động
các thuộc tính của một thực thể. Vấn đề mấu chốt là tập trung đến những hành vi cốt
yếu áp dụng chúng trong ứng dụng.
Chẳng hn như khách hàng hoặc nhân viên bán hàng cũng thể thực hin những hành
động sau:
Người ấy đi lại.
Người ấy i chuyện.
Tuy nhiên, những hành động này không liên quan đến ng dụng. Việc trừu tượng hóa dữ
liệu sẽ loại bỏ chúng.
1.2.2 Lớp (Class)
Trong ứng dụng mua bán xe, chúng ta đã xác định các thuộc tính các hành
động cần có để xuất mt hóa đơn cho mt khách hàng.
Các hành động và các thuộc tính này là chung cho mọi khách hàng mua xe. Ví thể, chúng
có thể được nhóm lại trong mt thực thể đơn nhất gọi là mt ‘lớp’.
Hãy khảo sát lớp tên ‘khách hàng’ dưới đây. Lớp này bao gồm mi thuộc tính
hành động đòi hỏi đối với mt khách hàng.
Tên khách hàng
Lớp Khách hàng
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 13
Địa chỉ khách hàng
Kiểu xe được bán
Nhân viên bán xe
Nhập tên
Nhập địa ch
Nhập kiểu xe được bán
Nhập tên nhân viên bán xe
Xuất hóa đơn
Định nghĩa
Một lớp một nh khái niệm về mt thực thể. mang tính cách tổng quát ch
không mang tính cách đặc thù.
Khi đnh nghĩa mt lớp, chúng ta muốn phát biểu rằng mt lớp sẽ phải mt tập hợp
các thuộc tính và các hành động riêng. Chẳng hn như một định nghĩa lớp dưới đây:
Lớp Con người
n
Chiều cao
Màu tóc
Viết
Nói
Lớp này định nghĩa thực thể ‘Con người’. Mọi thực thể thuộc kiểu ‘Con người’ sẽ đều có
những đặc tính và nhng hành động như đã được định nghĩa.
Một lớp định nghĩa mt thực th theo nhng thuộc tính những hành động chung. Hoặc
Những thuộc tính những hành động chung của một thực thể được nhóm li để tạo nên
mt đơn vị duy nhất gọi là mt lp. Hoặc Một lớp là một sự xác định cấp chủng loại của
các thực thgiống nhau.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 14
Một khi một lớp đã được định nghĩa, chúng ta biết được nhng thuộc tính những hành
động của nhng thực thể ‘trông ging’ như lớp này. Vì thế, tbản chất một lớp là mt
nguyên mẫu (prototype).
Một dụ khác về mt lớp liên quan đến việc mua bán xe hơi như sau:
Lớp Nhân viên bán hàng
n
Số lượng xe bán được
Tiền hoa hng
Nhập tên
Nhập số lượng xe bán được
Tính tin hoa hồng
Lớp trên định nghĩa các thuộc tính các hành động đặc trưng cho mi nhân viên bán xe
i.
1.2.3 Đối tượng (Object)
Một lớp mt nguyên mẫu phác ha những thuộc tính nhng hành động thể
của mt thực thể. Để thể sử dụng thực thể lớp định nghĩa, chúng ta phải tạo mt
‘đối tượng’ từ lớp đó.
Lớp một khái niệm, còn đối tượng một thể hiện được định nghĩa bởi lớp.
Hãy khảo sát lớp Khách hàngđược định nghĩa trên. Lớp này định nghĩa mi thuộc
tính và hành động gắn liền với mt khách hàng.
Khi mt người mua mt xe hơi mt cửa hàng, cửa hàng ấy mt khách hàng mới.
Vào thời điểm y, một đối tượng ging như lớp ‘Khách ng’ được tạo ra. Đối tượng y
sẽ phi có những giá trị thực đối với các thuộc tính ‘Tên’, ‘Địa chỉ’, ‘Kiểu xe’ …
Chẳng hạn n mt khách hàng tên ‘Mark’, sng ‘London’ đã mua một xe
kiểu ‘Honda Civic’ từ nhân viên bán hàng tên ‘Tom’. Như thế, ‘Mark’ mt đối
tượng của kiểu ‘Khách hàng’.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 15
Định nghĩa: Đối tượng là sự kết hợp giữa dữ liệu và thủ tục ( hay n gọi các
phương thức method) thao tác trên dữ liệu đó. Có thể đưa ra công thức phản ánh bn
chất k thuật lập trình hướng đối tượng như sau:
Đối tượng=Dữ liệu + Phương thức
Một đi tượng là một thực thể cụ thể (thông thường bạn có thsờ chạm, xem thy và cảm
nhận).
Kể từ lúc mt đối tượng hiện hữu, những thuộc tính của những giá trị xác định,
những hành động được đnh nghĩa cho đối tượng này được thực thi.
Trong mỗi mt đối tượng, các khía cạnh sau đây được xác định rõ:
Tình trạng (state).
Thái độ (behavior).
Chân tính (identity).
Hình: Trình bày hai đối tượng.
Mỗi đối tượng có những đặc tính riêng mô tả đối tượng ấy là gì, hoặc hành động ra sao.
Chẳng hn như những thuộc tính của mt đối tượng ‘Con người’ sẽ :
n.
Tuổi.
Trọng lượng.
Những thuộc tính của mt đối tượng ‘Xe hơi’ sẽ là:
Màu sắc.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 16
Kiểu xe.
Năm.
Một đối tượng ng thực hiện mt số hành động. Một xe hơi khả năng thực hiện
những hành động sau:
Khởi động.
Ngưng.
Chuyển động.
Để chuyển đổi giữa các đối tượng lập trình các đối tượng đời thực, cần phải kết hợp
các thuộc tính và các hành động của một đối tượng.
1.2.4 Thuộc nh
Chúng ta xác định các thuộc tính các hành động để định nghĩa mt lớp. Một khi các
thuộc tính được gán cho các giá trị, chúng mô tả một đối tượng. Hãy khảo sát lớp sau:
Các thuộc tính của lớp Khách
ng
Tên của khách hàng
Địa chỉ của khách hàng
Kiểu xe được bán
Nhân viên đã bán xe
Khi thuộc tính Tên’ được gán cho giá tr ‘Mark’ thì tả mt đi tượng xác định
được tạo t lớp ‘Khách hàng’.
Như thế, các thuộc tính nắm giữ các giá trị dữ liệu trong một đối tượng, chúng định nghĩa
mt đối tượng cụ thể.
Định nghĩa
Một thuộc tính mt đặc tính tả mt đối tượng.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 17
Bởi một lớp là một nguyên mẫu cho nên các thuộc tính trong một lớp không thể nm
giữ các g trị. Một thuộc tính thể được n một giá trị chỉ sau khi mt đối tượng dựa
trên lớp ấy được tạo ra.
Để thể lưu giữ nhng chi tiết của mt khách hàng, mt thể hiện(đối tượng) của lớp
‘Khách hàng’ phải được tạo ra. Các thuộc tính của mt đi tượng hin hữu chỉ khi đối
tượng ấy được tạo ra.
Mọi đối tượng của mt lớp phải cùng các thuộc tính.
Khảo sát ví dụ sau:
=
1.2.5 Hoạt động (Operation)
Các hành động khả thi, như được định nghĩa trong mt lớp, được gọi ‘các hoạt đng’.
Định nghĩa:
Các hoạt động c định các hành động cần phảit thực hiện của một đối tượng được tạo ra
tmt lớp. Chẳng hn như chúng ta không thể yêu cầu một hoạt đng ‘Mua một xe i
khác’ của mt đi tượng được tạo ra từ lớp ‘Khách hàng’.
Một lớp chỉ mt nguyên mẫu. thế, trong mt lớp mt hoạt động chỉ được định
nghĩa. Còn việc áp dụng hoạt động ấy chỉ xảy ra nơi các đối tượng riêng rẽ. Chẳng hạn
như hoạt động ‘Nhập Tên’ lớp “Khách hàng’ định nghĩa thể được thực hiện nơi
mt đối tượng o đó.
Tập hợp các hoạt động được yêu cầu cho tt cả các đối tượng trong cùng mt lớp như
nhau.
1.2.6 Phương thức (Method)
Các hoạt động định nghĩa các hành động khả thi thể được yêu cầu của mt đối tượng.
Một phương thức là sự thực thi thực tế của mt hoạt động.
Đối tượng được tạo từ lớp Con người
Mark
6 ft. 1 in.
Black
c thuc nh của lớp Con người
n
Chiều cao
Màu tóc
Một hoạt động mt dịch vụ được yêu cầu của mt đối tượng.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 18
Định nghĩa
Các phương thức xác đnh cách thức thao tác trên các dữ liệu của một đối tượng. Bởi
phương thức sự thực thi thực tế mt hoạt động, cho nên th được áp dụng cho
mt đối tượng. Một phương thức là một thuật toán, từng bước từng bước (step-by-step)
c định điều gì được thực hiện khi hoạt động ấy được yêu cầu.
Hãy khảo sát những hoạt động chung của một thực thể thuộc loại ‘Con người’: Đi, Nói.
Chỉ khi mt đối tượng cụ thể của loi ‘Con người’ được tạo ra thì các hành động ‘Đi’,
‘Nói’ mới được thực thi.
1.2.7 Thông điệp (Message)
Để yêu cầu mt hoạt đng cụ thể nào đó được thực hin, mt thông điệp được gởi tới đối
tượng nơi hoạt động này được định nghĩa.
Định nghĩa
Khi mt đối tượng nhận được mt thông điệp, thực hiện một phương thức tương ứng.
Chẳng hạn, mt đi tượng được to tlớp ‘Khách hàngđể nhập tên của người sử dụng.
Khi đối tượng nhận được thông điệp, nó tìm và thực thi phương thức ‘Nhập tên’.
Trong trường hợp mt ng ty, mi bộ phận được coi là mt đối tượng. Những thông tin
được chuyển tới được đón nhn từ mi bộ phận (hoặc qua thông báo liên bộ phận,
hoặc qua nhng chỉ thị miệng) tạo nên những thông điệp giữa các đối tượng. Nhng chỉ
thị này có thể được chuyển dịch thành những lời gọi hàm trong mt chương trình.
Các đ
i t
ượ
ng g
i thông đi
p cho nhau
Phương thức sự xác định về cách thức thực thi một hoạt động được yêu cầu.
Một tng điệp mt lời yêu cầu mt hoạt động.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 19
Trong nh trên ‘Kinh doanh’ ‘Kế toán’ là hai bphận khác nhau trong một công ty.
Hai bộ phận này được coi hai đối tượng khác nhau. Thông tin được truyền đi được
đón nhận giữa các bộ phận to nên các thông điệp giữa các đối tượng.
1.2.8 Sự kiện (Event)
Một sự kiện là một sự vic xảy ra cho mt đi tượng tại một thời điểm. Để đáp ứng lại sự
kiện ấy, đối tượng sẽ thực hiện mt hoặc nhiều phương thức.
Nói cách khác, mt sự kiện mt tác nhân đối tượng này y ra cho mt đối tượng
khác. Chẳng hạn như click chuột trái trên mt nút.
Để hiểu n các sự kin, hãy khảo sát dụ sau từ thực tế:
‘Một người sẽ thét lên khi bị thọc bằng một vật nhọn’.
‘Thc’ là sự kin gây ra sự phản ứng là ‘thét lên’.
Trong máy tính, mt người sử dụng nhấn một nút trên bàn phím một sự kiện chung. Sự
phản hồi đối với sự kiện này là việc hiển thị ký ttương ứng trên màn hình.
1.2.8 Phân biệt giữa lớp đối ợng
mt sự khác biệt thực sự giữa một lớp một đối tượng. Cần nhn thức sự khác
biệt này.
Một lớp định nghĩa mt thực thể, trong khi đó một đối tượng mt trường hợp của thực
thấy.
Đối tượng mt nh thực, trong khi lớp mt nh khái niệm - định nghĩa tất
cả các thuộc tính và các phương thức cần thiết của mt đối tượng.
Tất cả các đối tượng thuộc về ng một lớp cùng các thuộc tính các phương thức.
Một lớp là mt nguyên mẫu của mt đối tượng. Nó xác đnh các hành động khả thi các
thuộc tính cần thiết cho mt nm các đối tượng cụ thể.
1.2.9 Thiết lập (Construction) Hủy (Destruction)
1.2.9.1 Thiết lập
Một lớp chỉ cung cấp nhng định nghĩa về các thuộc tính và c phương thức khả
thi. Các thuộc tính và các phương thức thđược truy cập chỉ khi mt đối tượng dựa
trên mt lớp được tạo ra.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 20
Khi mt đối tượng mới được tạo, các thuộc tính của nó trở nên hiện thực thể được
gán giá trị. Tương tự, các phương thức đã được định nghĩa cũng được áp dụng.
Định nghĩa
Mỗi một lớp một hàm thiết lập.
Khảo sát li trường hợp cửa hàng bán xe hơi. Ngay từ lúc đầu chỉ định nghĩa các lớp.
Cho đến khi mt khách hàng mua mt xe hơi ti cửa hàng thì mt đối tượng mới ging
như lớp ‘Khách hàng’ mới được tạo.
Khi đối tượng này được tạo, mt số khoảng trống bộ nhđược cấp phát cho nhng thuộc
tính của để lưu trữ các giá trị được n cho các thuộc tính ấy (‘Tên’, ‘Địa chỉ’ …).
Hàm thiết lập thực hiện việc cấp phát này. Vào lúc này, mọi thuộc tính phương thức
của đối tượng sẵn sàng để sử dụng.
Tương tnhư trường hợp mt hc sinh nhập học tại mt trường học. Khi một học sinh
nhập học, mt vài hành động được thực hiện để nhận học sinhy vào trường. Đó là:
Xếp lớp cho học sinh ấy.
Ghi tên học sinh ấy vào danh sách.
Xếp chỗ ngồi.
Đây nhng hành động đồng loạt được thực hiện ngay lúc bắt đầu nhập học. Chúng
tương tự vi những hành động mà hàm thiết lập của một đối tượng thực hiện.
1.2.9.2 Hủy
Khi mt đi tượng không còn cần thiết nữa thì sẽ bị hủy b. Sẽ lãng phí i nguyên,
chng hn như bộ nhớ, nếu như tiếp tục để cho mt đối tượng tồn tại mt khi không
còn cần thiết.
Thiết lập mt tiến trình hiện thực hóa mt đối tượng.
Hàm thiết lập mt phương thức đặc biệt phải được gọi trước khi sử dụng bất kỳ
phương thức nào trong một lớp. Hàm Thiết lập khởi tạo các thuộc tính, cấp phát bộ
nhớ nếu cần.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 21
Định nghĩa
Một khi một đối tượng bị hủy tcác thuộc tính của không thể được truy cập, cũng
như không mt phương thức nào có thể được thực thi.
Chẳng hạn, trong trường hợp bán xe i, một khi nhân viên bán hàng bỏ nghề, những chi
tiết của người ấy không n liên hệ. thế, đối tượng tương ứng sẽ bị hủy. Điều này giải
phóng bộ nhớ đã cấp phát cho nhân viên bán hàng ấy. Khoảng trống này giờ đây thể
được tái sử dụng.
Hãy xem xét dụ về trường học trên đây. Khi một học sinh thôi học, tên của học sinh y
bị loại ra khi danh sách, và khoảng trống được gii phóng có thể được tái cấp phát.
Các hành động đồng loạt này tương tự với công việc của hàm hủy đối với một đối tượng.
1.2.10 Tính Bền vững (Persistence)
Hãy khảo sát trường hợp bán xe hơi. Những chi tiết của khách hàng được lưu trữ ngay
khi xe hơi đã được phân phi.Việc duy trì dữ liệu vẫn cần thiết cho đến khi dữ liệu được
chỉnh sửa hoặc hủy bỏ chính thức.
Định nghĩa
Cửa hàng bán xe lưu trữ chi tiết khách hàng vào mt file. Những chi tiết này sẽ tồn tại
trong file cho đến khi chúng bị hủy, hoặc bản thân file bị hủy.
Chúng ta đụng chạm tính bền vững mi ngày. Hãy xem việc sáng tác mt bài thơ. i t
dữ liệu tồn tại trong tâm trí của nhà t. Bao lâu nhà thơ n tồn tại t bấy lâu bài t
còn tồn tại. Nếu bài thơ muốn tồn tại ngay cả sau khi nt qua đời t phải được
viết ra giy.
Bài thơ được viết ra giy tạo nên sự bền vững. Bài thơ sẽ tồn tại bao lâu văn bản y còn
được duy trì. Bài thơ ấy không còn tồn tại khi tờ giy ấy bị rách, hoặc chữ nghĩa bị
xóa đi.
Hàm Hủy là một phương thức đặc biệt được dùng để hy bỏ mt đi tượng. Tiến trình
Hủy tiêu hy mt đối tượng giải phóng khoảng trống bộ nhmà hàm thiết lập đã cấp
phát cho nó. Hàm Hủy cũng triệt tiêu khả năng truy cập đến đối tượng ấy.
Tính Bền vững khả năng lưu trữ dữ liệu của mt đi tượng ngay cả khi đối tượng y
không còn tồn tại.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 22
1.2.11 Tính Đóng gói dữ liệu
Tiến trình trừu tượng hóa dữ liệu hỗ trợ cho việc xác định những thuộc tính những
phương thức thiết yếu.
Thông thường, các đối tượng sử dụng nhng thuộc tính và nhng phương thức không
được yêu cầu bởi người sử dụng đối tượng.
Chẳng hạn như trong trường hợp lớp Khách hàng’. Lớp y mt phương thức xuất hóa
đơn. Giả sử rằng khi hóa đơn được xuất, mt trong những chi tiết được in ra trên hóa đơn
ngày pn phối. Tuy nhiên chúng ta không biết thuc tính nào qua đó chúng ta thể
c định thông tin này.
Ngày phân phối được phát sinh bên trong đối tượng, được hin thị trên hóa đơn. Như
thế người sử dụng không nhận thức vcách thức mà ngày phân phối được hiển thị.
Ngày phân phối th được xử theo mt trong những cách sau:
Đó một giá tr được tính toán - Chẳng hạn, 15 ngày kể từ ny đặt hàng.
Đó một giá tr cố định Xe hơi được phân phối vào ngày mùng 2 mi tháng.
Đối tượng sử dụng những thuộc tính và những phương thức mang tính ni b. Bởi
những thuộc tính và những phương thức thể được che khuất khỏi tầm nhìn. Các đối
tượng khác những người sử dụng không nhận thức được các thuộc tính / hoặc các
phương thức như thế tồn tại hay không. Tiến trình che giấu các thuộc tính, các phương
thức, hoặc các chi tiết của việc thi hành được gọi là ‘đóng gói’ (encapsulation).
Định nghĩa
Việc đóng gói phân tích những khía cạnh thể truy cập từ bên ngoài vi những khía
cạnh chỉ được sử dụng trong nội bộ của đi tượng.
Ưu đim của việc đóng i là thtạo ra bất kỳ thuộc tính hay phương thức cần thiết để
đáp ứng đòi hỏi ng việc khi xây dựng một lp. Mặt khác, chỉ nhng thuộc tính /
hoặc những phương thức có thể được truy cập từ bên ngoài lớp thì mới nhìn thấy.
Đóng gói tiến trình che giấu việc thực thi những chi tiết của một đối tượng đối với
người sử dụng đối tượng ấy.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 23
Một dụ khác về việc đóng i lớp ‘Nhân viên bán hàng’ đã được định nghĩa trên.
Khi phương thức tính tiền hoa hng được thực thi, người sử dụng không biết chi tiết của
việc tính toán. Tất cả nhng h biết chỉ tổng số tin hoa hồng mà h phải trả cho
nhân viên bán hàng.
Một trường hợp vđóng i mà chúng ta gặp trong đời sống hằng ngày việc giao dịch
kinh doanh một cửa hàng. Khách hàng u cầu sản phẩm X. Họ được trao cho sản
phẩm X, và h phải trả tiền cho sản phẩm ấy. Sau khi khách hàng yêu cầu sản phẩm,
người bán hàng thực hin những hành động sau:
Kiểm tra mặt hàng trên kệ hàng.
Giảm số lượng mặt hàng trong bảng kim sau khi bán.
Tuy nhiên, khách hàng không được biết những chi tiết này.
1.2.12 Tính thừa kế
Hãy khảo sát các lớp sau:
Lớp Sinh viên
Lớp Nhân viên
Lớp Khách ng
n
n
n
Địa ch
Địa ch
Địa chỉ
Điểm môn 1
Lương
Kiểu xe đã bán
Điểm môn 2
Chc vụ
Nhập tên
Nhập tên
Nhập tên
Nhập địa chỉ
Nhập địa ch
Nhập địa ch
Nhập kiểu xe
Nhập điểm
Nhập chức vụ
Xuất hóa đơn
Tính tng điểm
Tính lương
Trong tất cả ba lớp, chúng ta thấy mt vài thuộc tính hoạt động chung. Chúng ta
muốn nhóm nhng thuộc tính và những hoạt động ấy li, định nghĩa chúng trong mt
lớp ‘Người’.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 24
Lớp Người
n
Địa ch
Nhập tên
Nhập địa ch
Ba lớp ‘Sinh viên’, Nhân viên’ ‘Khách hàng’ những thành phần ging lớp
‘Người’. i cách khác, ba lớp ấy tt cả các thuộc tính các phương thức của lp
‘Người’, ngoài ra chúng còn có nhng thuộc tính và những phương thức riêng.
Chúng ta cần phải định nghĩa lớp ‘Người’ sử dụng nó trong khi định nghĩa các lớp
‘Sinh viên’, ‘Nhân viên’ và Khách hàng’.
Chúng ta y dựng mt lớp Người’ với nhng thuộc tính những hoạt động như đã
tnh bày hình trên. Kế tiếp, chúng ta xây dựng lớp ‘Khách hàng’ bao gồm lớp ‘Người’
cộng với những thuộc tính và những phương thức riêng.
Chúng ta thể đnh nghĩa các lớp ‘Sinh viên‘Nhân viêntheo cùng cách thức trên.
Như thế, cả ba lớp ‘Khách hàng’, ‘Sinh viên’ Nhân viên’ đều chia sẻ những thuộc
tính và những phương thức mà lớp ‘Người’ cung cấp.
Lớp Sinh viên
Lớp Nhân viên
Lớp Khách ng
Điểm môn 1
Lương
Kiểu xe bán được
Điểm môn 2
Chc vụ
Nhập kiểu xe
Nhập điểm
Nhập chức vụ
Xuất hóa đơn
tính tổng điểm
Tính lương
Theo ngôn ngữ hướng đối tượng, lớp Khách hàng được gọi thừa kế lớp ‘Người’.
Định nghĩa: Tính thừa kế cho phép mt lớp chia sẻ các thuộc tính các phương thức
được định nghĩa trong một hoặc nhiều lớp khác.
hai khái niệm quan trọng khác liên kết vi tính thừa kế. Lớp ‘Khách hàng’ là lp
‘Ngườicộng thêm cái khác. Như thế, lớp ‘Khách hàng’ tất cả các thuộc tính các
phương thức được định nghĩa trong lớp ‘Người’ cộng với các thuộc tính các hoạt động
của riêng nó.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 25
Trong dụ này, lớp Khách hàngđược gọi ‘lớp con’ (subclass).
Định nghĩa: Lớp thừa hưởng tmt lớp kc được gọi Subclass.
Trong ví dụ trên, lớp ‘Người’ được coi là ‘lớp trên’ (superclass).
Định nghĩa: Một Superclass mt lớp các đặc tính của được một lớp khác thừa
hưởng.
Hãy xem xét dụ về lớp ‘Các động vật’ hình dưới đây ‘Các động vậtlà lớp trên ng
các lớp khác kế thừa. Chúng ta có mt dãy các lớp trung gian ‘Côn trùng’, ‘Hữu
nhũ’, ‘Bò sát’, ‘Lưỡng cư’ - mà dãy các lớp dưới kế thừa.
Các lớp ‘Côn trùng’, ‘Hữu nhũ’, ‘Bò sát’, ‘Lưỡng cư’ là nhng lớp con của lớp trên ‘Các
động vật’. Như thế, những lớp này tt cả nhng thuộc tính các hoạt động của lớp
‘Các động vật’, cng thêm những thuộc tính và những phương thức của riêng chúng.
Lớp ‘Hữu nhũ’ là lớp mà các lớp ‘Con người’ và ‘Khác con người’ thừa kế. Như thế, các
lớp ‘Con người’ và ‘Khác con người’ là các lớp con của lớp trên ‘Hữu nhũ’.
Côn tng
Hữu
nhũ
sát
Lưỡng
Con người Khác con người
1.2.13 Tính Đa Thừa kế
Trong tất cả các dụ trên, mt lớp thừa kế chỉ từ một lớp. Ngay cả trong dụ thừa kế
về các loi phương tiện di chuyển, mi lớp con chỉ một lớp cha. Trường hợp như thế
gọi là ‘thừa kế đơn’ (single inheritance).
Trong ‘đa thừa kế’, mt lớp con thừa kế từ hai hay nhiều lớp cha.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 26
Hãy khảo sát dụ sau:
Lớp Đường thẳng
Lớp Đưng tròn
Lớpnh ảnh
Lớp Vẽ một hình
Khởi điểm
n kính
Hình ảnh
Điểm tận cùng
Tâm điểm
Vẽnh ảnh
Nhậnnh vẽ
Vẽ đường thẳng
+
Vẽ đường tròn
+
=
Vẽ hình
Trong hình trên, chúng ta đã xây dụng một lớp ‘Vẽ mt hình’, lớp này thừa hưởng ba
lớp: Đường thẳng’, ‘Đường tròn’, ‘Hình ảnh’. Như thế lớp Vẽ mt nh’ kết hợp chức
năng của ba lớp trên thêm o chức năng được định nghĩa bên trong nó.
Lớp ‘Vẽ mt hình’ một dụ về tính đa thừa kế.
thể sử dụng tính đa thừa kế để xây dựng một lớp mi, lớp này dẫn xuất chức năng của
t mt vài lớp khác. Như thế, xét theo góc cạnh của người sử dụng lớp mới này, chỉ
cần một lớp cung cấp tất cả các chức năng. Như vậy, họ không cần phải sử dụng
nhiều đối tượng khác nhau.
Sự thuận lợi quan trọng nhất của tính thừa kế là nó thúc đẩy việc tái sdụng chương
trình.
Trong dụ trên, chúng ta ba lớp ‘Đường thẳng’, ‘Đường tròn’ ‘Hình ảnh’. Gi
thiết rằng ba người khác nhau xây dựng ba lớp này riêng biệt. y giờ, người sử dụng
cần xây dựng một lớp để vđường thẳng, vẽ đường tròn cũng như hiển thị nh nh. Vì
thế họ tìm kiếm xem lớp nào đáp ứng mt hoặc tt cả các yêu cầu đó. Nếu những
lớp cung cấp chức năng tha yêu cầu t người sdụng sthừa kế nhng lớp đó đtạo
mt lớp mới.
Giđây người sử dụng chỉ còn phải viết chương trình cho nhng đặc tính chưa sau
tiến trình thừa kế. Người sử dụng thể sử dụng chính ba lớp trên. Tuy nhiên, sự thừa kế
cung cấp một bó những chức năng hỗn độn trong mt lớp.
1.2.14 Tính Đa nh
Trong mt chương trình cấu trúc (a structured program), mt phương thức chỉ ng
dụng cho mt đối tượng. Chẳng hạn xét toán t‘Cộng’. Toán tnày chỉ tính tổng của hai
số nguyên. Khi truyn hai giá trị 2 3 t hiển thị 5. Chúng ta không thể mt loi
toán tử ‘Cộng’ để nh tổng của hai gtrị văn bản (text) ‘Hello!’ ‘How are you? để
được chuỗin bn kết quả ‘Hello! How are you?’
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 27
Trong hệ thống hướng đối tượng t tình huống tả trên th.
Định nghĩa
Với tính đa hình, nếu ng mt phương thức ứng dụng cho các đối tượng thuộc các lớp
khác nhau t đưa đến những kết quả khác nhau. Bản chất của sự việc cnh phương
thức này bao gồmng mt số lượng các tham số.
Tính đa hình mt trong những đặc tính quan trọng nhất của hệ thống hướng đối tượng.
Một dụ khác phương thức hiển thị. Tùy thuộc o đi tượng tác đng, phương thức
ấy thể hiển thị mt chuỗi, hoặc vẽ một đường thẳng, hoặc hiển thị mt nh nh. y
khảo sát hình sau:
Lớp
: Hình thể Các lớp con
c phương thức:
Vẽ
Lớp nh thể và các lớp con
Hình trên cho thấy rằng ‘Vẽ’ là một phương thức được chia sgiữa các lớp con của lớp
‘Hình thể’. Tuy nhiên, phương thức Vẽ được ng dụng cho hình hộp sẽ khác vi hình
êlip.
Tính đa hình hỗ tr tính đóng gói.
Xét trên mc độ người sử dụng, họ chỉ cần một phương thức ‘Vẽ’ của lớp ‘Hình thể’.
Còn cách thức mà phương thức Vẽđược thực thi cho các trường hợp khác nhau thì họ
không cần biết.
Tính đa hình cho phép một phương thức có các cách thể hiện khác nhau trên nhiều loại
đối tượng khác nhau.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 28
1.3 Các ưu đim ca lập trình hưng đối tượng
1.3.1. Các ưu điểm của lập trình hướng đối ợng
LTHĐT đem li mt số li thế cho người thiết kế ln nời lập trình. ch tiếp
cận hướng đối tượng giải quyết được nhiều vấn đề tồn tại trong quá trình phát trin phần
mềm tạo ra được những phần mềm độ phức tạp chất lượng cao. Phương pháp
này mở ra triển vng to lớn cho người lập trình. Nhưng ưu điểm cnh của LTHĐT là:
1. Thông qua nguyên kế thừa, chúng ta thể loại bỏ được những đoạn chương
tnh lặp lại trong quá trình tả các lớp thể mở rộng khả năng sử dụng của các
lớp đã xây dựng không cần phải viết lại.
2. Chương trình được xây dựng từ những đơn thể( đi tượng) trao đổi với nhau
nên việc thiết kế lập trình sẽ được thực hin theo quy trình nhất định chứ không phải
dựa vào kinh nghiệm kỹ thuật như trước nữa. Điều này đảm bảo rút ngắn được thời
gian xây dựng hệ thng và tăng năng xuất lao động.
3. Nguyên đóng gói hay che dấu thông tin giúp người lập trình tạo ra được
những chương trình an toàn không bị thay đổi bởi những đoạn chương trình khác.
4. thể xây dựng được ánh xạ các đối tượng của bài toán vào đối tượng chương
trình.
5. Cách tiếp cận thiết kế đặt trọng tâm vào dữ kiệu, giúp chúng ta xây dựng được
hình chi tiết dẽ ng cài đặt hơn.
6. Các hệ thng hướng đối tượng dẽ mở rộng, nâng cấp thành những hệ lớn
7. Kỹ thuật truyền thông báo trong vic trao đi thông tin giữa các đối tượng làm
cho việc mô t giao diện với các hệ thống bên ngoài trở nên đơn giản hơn,
8. th quản được độ phức tạp của sản phẩm phần mềm.
1.3.2. Mốt số ngôn ngữ lập trình hướng đối ợng
Lập trình hướng đối tượng không phải đặc quyền của mt nn ngữ đặc biệt
o. Cũng ging như k thuật lập trình cấu trúc, các khái niệm trong lập trình hướng
đối tượng được thể hiện trong nhiều ngôn ngữ lập trình khác nhau. Những ngôn ngữ cung
cấp được những khả năng lập trình hướng đối tượng được gọi ngôn ngữ lập trình
hướng đối tượng. Tuy vẫn những ngôn ngữ chỉ cung cấp khả năng tạo lớp đối
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 29
tượng mà không cho phép kế thừa, do đó hn chế khả năng lập trình hướng đối tượng.
Hình dưới đây cho ta mt cái nhìn tổng quan về s phát triển các ngôn ngữ lập trình
hướng đối tượng.
Đây hình ảnh sự phát triển các ngôn ngữ lập trình hướng đối tượng
Các nn ngữ SIMULA, SMALLTALK, JAVA, C# thuộc h ngôn ngữ lập trình
hướng đối tượng thuần khiết, nghĩa không cho phép phát trin các chương trình cấu
trúc trên các nn ngữ loại này.
Lập trình hướng đối tượng mt trong nhng thuật ngữ được nhắc đến nhiều nhất
hiện nay trong ng nghệ phần mềm được ứng dụng để phát trin phần mềm trong
nhiều lĩnh vực khác nhau. Trong số đó, ứng dụng quan trọng nổi tiếng nhất hin nay
thiết kế giao din với người sử dụng. kiểu như Windows. Các hệ thống tin quản lý trong
thực tế thường rất phức tạp, chứa nhiều đối tượng với nhiều thuộc tính và hàm phức tạp.
Để gii quyết những hệ thống thông tin phức tạp như thế , lập trình hướng đối tượng tỏ ra
rất hiệu quả. Các lĩnh vực ng dụng phù hợp với kỹ thuật lập trình hướng đối tượng
thliệt kê như dưới đây:
Các hệ thng làm việc theo thời gian thực
Các hệ hình hoá hoặc phỏng các quá trình
Các hệ cơ sở dữ liệu hướng đối tượng
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 30
Các hệ siêu văn bản, đa phương tiện
Các hệ thống trí tuệ nhân tạo hệ chuyên gia
Các hệ thống song song và mng -ron
Các hệ tự động hoá n phòng hoặc trợ giúp quyết định
Các hệ CAD/CAM.
1.3.3. Ngôn ngữ lập trình C#
C# là một ngôn ngữ lập trình hướng đối tượng được phát triển bi Microsoft, là
phần khởi đầu cho kế hoạch .NET của h. Tên của ngôn ngữ bao gồm ký tthăng theo
Microsoft nhưng theo ECMA C#, chỉ bao gồm dấu số thường. Microsoft phát trin C#
dựa trên C++ Java. C# được miêu tlà ngôn ngữ được sự cân bằng giữa C++,
Visual Basic, Delphi và Java.
C#, theo mt hướng nào đó, ngôn ngữ lập trình phản ánh trực tiếp nhất đến
.NET Framework mà tất cả các chương tnh .NET chạy, nó phụ thuộc mnh mo
Framework này. Các loại dữ liệu sở là những đối tượng, hay được gọi là garbage-
collected, nhiều kiểu trừu tượng khác chng hạn như class, delegate, interface,
exception, v.v, phản ánh rõ ràng những đặc trưng của .NET runtime.
So sánh với C và C++, nn ngữ này bị giới hn và được nâng cao mt vài đặc
điểm nào đó, nhưng không bao gồm các giới hạn sau đây:
Các con trỏ chỉ thể được sử dụng trong chế độ không an toàn. Hầu hết các đối
tượng được tham chiếu an toàn, các phép tính đều được kiểm tra tràn bộ đệm. c con
trỏ chỉ được sử dụng để gọi các loại kiểu giá trị; còn nhng đối tượng thuộc bộ thu rác
(garbage-collector) t chỉ được gọi bằng cách tham chiếu.
+ Các đối tượng không thể được giải phóng tường minh.
+ Chỉ đơn kế thừa, nhưng thể i đặt nhiều interface trừu tượng (abstract
interfaces). Chc năng này làm đơn giản hóa sự thực thi của thời gian thực thi.
+ C# thì an-toàn-kiểu (typesafe) hơn C++.
+ pháp khai báo mảng khác nhau("int[] a = new int[5]" thay vì "int a[5]").
+ Kiểu th tự được thay thế bằng tên min không gian (namespace).
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 31
liệu.
+ C# không tiêu bản.
+ thêm Properties, các phương pháp thể gi các Properties để truy cập dữ
+ reflection.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 32
Bài 2: Lp đi tưng( 1)
2.1. Lớp
2.1.1. Định nghĩa lớp
Chúng ta đã được nghiên cứu ngôn ngữ lập trình C# trong các học phần trước và
đã thảo luận rất nhiều kiểu dữ liệu bn của ngôn ngữ C# như: int, long char... Tuy
nhiên trái tim và linh hồn của C# khả ng to ra những kiểu dữ liệu mi, phức tạp.
Người lập trình tạo ra các kiểu dữ liệu mới bng cách xây dựng các lớp đối tượng và đó
cũng chính các vấn đề chúng ta cần thảo luận trong chương này.
Đây là khả năng để tạo ra những kiểu dữ liệu mới, mt đặc tính quan trọng của
ngôn ngữ lập trình hướng đối tượng. Chúng ta thể xây dựng những kiểu dữ liu mi
trong ngôn ngữ C# bng cách khai báo định nghĩa những lớp. Ngoài ra ta cũng thể
định nghĩa các kiểu dữ liệu với nhng giao diện (interface) sẽ được bàn trong các phn
sau. Thể hiện của một lớp được gọi là những đối tượng (object). Những đối tượng này
được tạo trong b nhớ khi chương trình được thực hiện.
Sự khác nhau giữa mt lớp một đối tượng cũng ging như sự khác nhau giữa
khái nim giữa loài o mt con mèo Mun đang nằm bên chân của ta. Chúng ta
không thể đụng chạm hay đùa giỡn vi khái niệm mèo nhưng thể thực hiện điều đó
được với o Mun, là mt thực thể sống động, ch không trừu tượng như khái niệm
họ loài o. Một họ mèo mô tnhng con o các đặc tính: trọng lượng, chiều
cao, màu mắt, u lông,...chúng ng hành động như ăn ngủ, leo trèo,...mt con
o, dụ như mèo Mun chẳng hạn, cũng trọng lượng xác đnh là 5 kg, chiều cao
15 cm, màu mắt đen, lông đen...Nó cũng có những khnăng như ăn ngủ leo trèo,..
Lợi ích to ln của những lớp trong ngôn ngữ lp trình khả ng đóng gói các
thuộc tính tính chất của mt thực thể trong mt khối đơn, t nghĩa, tkhả năng duy
t .
dụ: Khi chúng ta muốn sắp nội dung những thể hiện hay đối tượng của lớp điều
khiển ListBox trên Windows, chỉ cần gọi các đối tượng này thì chúng sẽ tsắp xếp, còn
việc chúng làm ra sao t ta không quan tâm, cũng chỉ cần biết by nhiêu đó thôi.
Đóng i cùng vi đa hình (polymorphism) kế thừa (inheritance) là các thuộc tính
chính yếu của bất k mt ngôn ngữ lập trình hướng đối tượng nào.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 33
Trong chương này chúng ta tìm hiểu các đặc tính của ngôn ngữ lập trình C# để
xây dựng các lớp đối tượng. Thành phần của mt lớp, các hành vi các thuộc tính, được
xem như thành viên của lớp (class member). Tiếp theo là khái niệm về phương
thức(method) được dùng để định nghĩa hành vi của một lớp, trạng thái của c biến
thành viên hoạt động trong mt lớp. Một đặc tính mới nn ngữ C# đưa ra để y
dựng lớp là khái niệm thuộc tính (property), thành phần thuộc tính y hoạt động giống
như cách phương thức để to mt lớp, nhưng bản chất của phương thức này tạo mt
lớp giao diện cho bên ngoài tương tác với biến thành viên mt cách gián tiếp, ta sẽ bàn
sâu vn đề này trong chương.
Định nghĩa lớp: Để định nghĩa một kiểu dliệu mới hay một lớp đầu tiên phải khai báo
rồi sau đó mới định nghĩa các thuộc tính phương thức của kiểu dữ liệu đó. Khai báo
mt lớp bằng cách sử dụng từ khoá class. Cú pháp đầy đủ của khai báo một lớp như sau:
sau
Thành phần thuộc tính của đối tượng sẽ được trình bày chi tiết trong các phần
Thành phần bổ sung truy cập ng sẽ được trình bày tiếp ngay mục dưới.
Đnh danh lớp chính tên của lớp do người xây dựng chương trình tạo ra.
Lớp sở lớp đối tượng sẽ kế thừa để phát trin ta sẽ bàn sau.
Tất cả các thành viên của lớp được định nghĩa bên trong thân của lớp, phần thân
này sẽ được bao bọc bởi hai dấu ({}).
Trong C#, mi chuyện đều xy ra trong mt lớp. Như các dụ chúng ta đã
tìm hiểu, các hàm điều được đưa o trong một lớp, kể cả hàm đầu vào của chương trình
(hàm Main()):
[Thuộc tính] [Bổ sung truy cập] class <Định danh lớp> [: Lớp sở]
{
<Phần thân của lớp: bao gồm định nghĩa các thành phần dữ liệu và
phương thức hành động >
}
public class Tester
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 34
Điều cần i đây là chúng ta chưa to bất cứ thể hiện nào của lớp, tức là tạo đối
tượng cho lớp Tester. Điều gì khác nhau giữa mt lớp và thể hiện của lớp? để trả lới cho
câu hỏi này chúng ta bắt đầu xem xét sự khác nhau giữa kiểu dữ liệu int một biến kiểu
int . Ta có viết như sau:
Ta không thgán giá trị cho một kiểu dữ liệu, thay vào đó ta chỉ được gán dữ liệu
cho một đối tượng của kiểu dữ lịêu đó, trong trường hợp trên đối tượng biến var1. Khi
chúng ta tạo một lớp mới, đó chính việc đnh nghĩa các thuộc tính hành vi của tt cả
các đối tượng của lớp. Giả schúng ta đang lập trình để tạo các điều khin trong các ứng
dụng trên Windows, các điều khiển này giúp cho người ng tương tác tốt với Windows,
như ListBox, TextBox, ComboBox,...Một trong những điều khiển thông dụng
ListBox, điều khin này cung cấp mt danh sách liệt các mục chn cho phép người
dùng chọn các mc tin trong đó. ListBox này cũng các thuộc tính khác nhau như:
chiều cao, bề dày, vị trí, màu sắc thể hiện các hành vi của chúng như: chúng thể
thêm bới mục tin, sắp xếp,...
Ngôn ngữ lập trình hướng đối tượng cho phép chúng ta to kiểu dữ liệu mới lớp
ListBox, lớp này bao bc các thuộc tính cũng như khả năng như: các thuộc tính height,
width, location, color, các phương thức hay nh vi như Add(), Remove(), Sort(),...
Chúng ta không th gán dữ liệu cho kiểu ListBox, thay o đó đầu tiên ta phải to một
đối tượng cho lớp đó:
ListBox myListBox;
Một khi chúng ta đã to một thể hiện của lớp ListBox t ta thể gán dữ liệu cho thể
hiện đó. Tuy nhiên đoạn lnh trên chưa thể tạo đối tượng trong bộ nhớ được, ta sẽ bàn
{
public static int Main()
{ // ... }
}
int var1 = 10;
tuy nhiên ta không thể viết được
int = 10;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 35
tiếp. Bây gi ta sẽ tìm hiểu cách tạo một lớp tạo các th hiện thông qua dụ minh họa
2.1a. dụ này tạo một lớp chức năng hiểu thị thời gian trong mt ngày. Lớp này
hành vi th hin ngày, tháng, năm, giờ, phút, giây hiện hành. Để làm được điều trên t
lớp này 6 thuộc tính hay còn gọi biến thành viên, cùng với mt phương thức n
sau:
dụ 2.1a: Tạo một lớp Point đơn giản như sau.
using System;
public class Point
{
private int x;
private int y;
public void Init(int ox,int oy)
{
x = ox;
y = oy;
}
public void Move(int dx, int dy)
{
x += dx;
y += dy;
}
public void Display()
{
Console.WriteLine("Toa do la:({0},{1})",x,y);
}
}
public class Tester
{
static void Main()
{
Point t = new Point();
t.Init(2, 3);
t.Display();
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 36
Kết quả:
Toa do:(2,3)
Toa do:(0,4)
2.1.2 Thành phần dữ liệu
Cách khai báo các thành phần dữ liệu ging như khai báo biến:
[Thuộc tính] [Bổ sung truy cập] Kieudulieu Tenthanhphan;
Kieudulieu thể các kiểu dữ liu sở (int, float, char, double .... ), các kiểu dữ liệu
do người ng định nghĩa(struct,union,...) hoặc đối tượng thuộc một lớp đã định nghĩa
trước đó.
Chú ý:
Không được dùng trực tiếp các lớp để khai báo kiểu thành phần dữ liệu thuộc vào bản
thân lớp đang được định nghĩa.
Các biến thành viên thđược khởi tạo trực tiếp khi khai báo trong quá trình khi
to, thay phải thực hiện việc khi tạo các biến trong bkhởi dựng. Để thực hiện việc
khởi tạo này rất đơn gin là việc sử dụng phép gán giá trị cho một biến:
int x = 2; // Khởi tạo
int y = 3; // Khởi tạo
Việc khởi tạo biến thành viên sẽ rất ý nghĩa, khi xác định giá tr khởi tạo như vậy
t biến sẽ không nhận gtrị mặc định mà trình biên dịch cung cấp. Khi đó nếu các biến
này không được gán lại trong các phương thức khi dng thì sẽ giá trị ta đã
khởi tạo.
t.Move(-2, 1);
t.Display();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 37
dụ 2.1b: Minh hoạ sử dụng khởi tạo biến thành viên.
using System;
class ThoiGian
{
int nam=1900;
int thang=1;
int ngay=1;
int gio=0;
int phut=0;
int giay=0;
public void KhoiTao(DateTime t)
{
nam = t.Year;
thang = t.Month;
ngay = t.Day;
gio = t.Hour;
phut = t.Minute;
giay = t.Second;
}
public void Hien()
{
Console.WriteLine("{0}:{1}:{2} ngay {3}/{4}/{5}",
gio, phut, giay, ngay, thang, nam);
}
}
public class Tester
{
static void Main()
{
ThoiGian t = new ThoiGian();
Console.Write(Thoi gian:”); t.Hien();
t.KhoiTao(DateTime.Now);
Console.Write(Hien tai la:”); t.Hien();
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 38
Kết qu:
Thoi gian: 0:0:0 ngay 1/1/1900
Hien tai la: 11:01:25 ngay 31/01/2007
Nếu không khởi tạo giá trị của biến thành viên t bộ khởi dựng mặc định sẽ khởi tạo giá
trị là 0 mặc định cho biến thành viên có kiểu nguyên.
2.1.3 Phương thức
Hàm được khai báo trong định nghĩa của lớp được gọi là hàm thành phn hay phương
thức của lớp. Các hàm thành phần thể truy nhập đến các thành phần dữ liệu và các
hàm thành phn khác trong lớp và các hàm thành phần của lớp khác nếu được phép.
2.1.4. Các từ khoá đặc tả truy cập
Thuộc tính truy cập quyết định khả năng các phương thức của lớp bao gồm việc
các phương thức của lớp khác thể nhìn thấy sử dụng c biến thành viên hay những
phương thức n trong lớp. Bảng 2.1.5 m tt c thuộc tính truy cập của một lớp trong
C#.
Thuộc nh
Giới hạn 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ởi bất trong các phương thức của các lớp khác.
private
Thành viên trong một lớp A được đánh dấu là private thì chỉ được
truy cập bới các phương thức của lớp A.
protected
Thành viên trong lớp A được đánh dấu là protected t chỉ được các
phương thức bên trong lớp A và những phương thức của lớp dẫn
xuất từ lớp A truy cập.
internal
Thành viên trong lớp A được đánh dấu là internal thì được truy cập
bởi bất cứ lớp nào trong cùng khi hợp ngữ với A
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 39
protected internal
Thành viên trong lớp A được đánh dấu là protected internal được
truy cập bởi các phương thức của lớp A, các phương thức của lớp
dẫn xuất của lớp A, và bất cứ lớp nào trong cùng khối hợp ngữ với
lớp A.
Mong muốn chung thiết kế các biến thành viên của lớp thuộc tính private.
Khi đó chỉ phương thức thành viên của lớp truy cập được giá tr của biến. C# xem
thuộc tính private là mặc định nên trong dụ 2.1b ta không khai o thuộc tính truy cập
cho 6 biến nên mặc định chúng là private:
// Các biến thành viên private
int Nam;
int Thang;
int Ngay;
int Gio;
int Phut;
int Giay;
Trong lớp ThoiGian hai phương thức thành viên Hien KhoiTao được khai
o là public nên bất kỳ lớp nào cũng có thể truy cập được.
Chú ý:
Thói quen lập trình tốt khai o tường minh các thuộc tính truy cập của biến
thành viên hay các phương thức trong một lớp. Mặc chúng ta biết chắc chắn rằng các
thành viên của lớp được khai báo private mặc định. Vic khai báo tường minh này sẽ
làm cho chương trình dễ hiểu, rõ ràng và tự nhiên hơn.
Các thành phần trong khái báo lớp được sắp xếp hết sức tùy ý. Nhưng chúng ta
lên gop các thành phần dữ liệu vào mt chỗ, các phương thức vào một ch
dụ 2.1.5: Nhập vào độ dài ba cạnh của mt tam giác rồi tính diện tích của tam giác
cho biết đó là tam giác gì
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 40
using System;
public class TamGiac
{
private double a, b, c;/*độ dài ba cạnh*/
public void nhap()
{
/*nhập o ba cạnh của tam giác, có kiểm tra điều kiện*/
do
{
Console.Write("Nhap vao canh a=");
a = Convert.ToDouble(Console.ReadLine());
Console.Write("Nhap vao canh b=");
b = Convert.ToDouble(Console.ReadLine());
Console.Write("Nhap vao canh c=");
c = Convert.ToDouble(Console.ReadLine());
} while (a + b <= c || b + c <= a || c + a <= b);
}
public void hien()
{
Console.WriteLine("Do dai ba canh:a={0},b={1},c={2}", a, b, c);
/* gọi hàm thành phn bên trong mt hàm thành phần khác cùng lớp */
Console.WriteLine("Dien tich tam giac : {0}", dientich());
switch (loaitg())
{
case 1: Console.WriteLine("Tam giac deu"); break;
case 2: Console.WriteLine("Tam giac vuong can"); break;
case 3: Console.WriteLine("Tam giac can"); break;
case 4: Console.WriteLine("Tam giac vuong"); break;
default: Console.WriteLine("Tam giac thuong"); break;
}
}
private double dientich()
{
return (0.25 *Math.Sqrt((a + b + c) * (a + b - c) * (a - b + c) * (-a + b + c)));
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 41
2.1.5. Khai báo đối ợng, mảng đối ợng
Một lớp sau khi được định nghĩa thxem như là mt kiểu dữ liệu đối tượng
th dùng để khai báo các biến, mảng đối tượng. Cách khai báo biến, mng đối tượng
giống như khai báo các biến bình thường khác:
C1: Định _danh _lớp Tên_đối_tượng = new Định_danh_lớp([tham số]);
C2: Định _danh _lớp Tên_đối_tượng;
…………………………
Tên_đối_tượng =new Định_danh_lớp([tham số]);
}
private int loaitg()
{
if (a == b || b == c || c == a)
if (a == b && b == c)
return 1;
else if (a * a == b * b + c * c || b * b ==a * a + c * c || c * c == a * a + b * b)
return 2;
else return 3;
else if (a * a == b * b + c * c || b * b ==a * a + c * c || c * c == a * a + b * b)
return 4;
else return 5;
}
}
public class Tester
{
static void Main()
{
TamGiac t=new TamGiac ();
t.nhap();
t.hien();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 42
Ví dụ: Point A, B; // Khai o hai đối tượng A,B
A= new Point();
Point []a = new Point[5]; // Khai báo mng con trỏ đối tượng
// Xin cấp phát vùng nhớ cho tùng con trỏ đối tượng và gán giá tr cho các thành
phần dữ liệu
for (int i = 0; i < a.Length; ++i)
{
a[i] = new Point();
}
Khi khái báo một đối tượng máy sẽ cung cấp vùng dữ liệu riêng cho từng đối tượng (
các đối tượng khác hay các hàm bên ngoài không được truy xuất nêu không cho phép)
các đối tượng dùng chung nhau định nghĩa hàm.
Lớp
2.1.6. Định nghĩa chồng phương thức
Ta biết rằng trong mt số ngôn ngữ lập trình như: Pascal, C,..Khi chúng ta định nghĩa
các hàm t các hàm phải khác tên nhau. Trong C# cho phép chúng ta định nghĩa các
phương thức trùng tên nhau. Ta gọi đó định nghĩa chồng phương thức. Khi các phương
thức được định nghĩa trùng tên nhau t giữa các phương thức phải khác nhau về kiểu giá
trị trả về, số lượng đối kiểu của các đi. Ta thy rằng sự khác nhau đó là cần thiết để
khi dch chương trình thì chương trình dch n cứ vào đó mà phân biết gia các hàm tùy
thuộc vào đối mà chúng ta truyền o cho hàm.
Khung DL
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 43
dụ 2.1.6: Xây dựng chương trình tìm gtrị của mt dãy số nguyên
using System;
public class TimMax
{
int n;
int[] a;
public void KhoiTao()
{
Console.Write("Nhap so phan tu cua mang n =");
n = Convert.ToInt16(Console.ReadLine());
a = new int[n];
for (int i = 0; i < n; ++i)
{
Console.Write("a[{0}]=", i);
a[i] = Convert.ToInt16(Console.ReadLine());
}
}
public void KhoiTao(int on)
{
n=on;
Console.WriteLine("So phan tu cua mang n={0}", n);
a = new int[n];
for (int i = 0; i < n; ++i)
{
Console.Write("a[{0}]=", i); a[i] = Convert.ToInt16(Console.ReadLine());
}
}
public int Max(int x, int y)
{
return (x > y ? x : y);
}
public int Max()
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 44
Chú ý:
Mt phương thc th gi đến mt phương thc cùng tên vi nó.
Trong trường hp có các hàm trùng tên trong chương trình, vic xác đnh
phương thc nào được gi do chương trình dch đm nhim căn cvào đi tương
ng chúng ta truyn o.
2.1.7. Thuộc tính thủ tục thuộc nh
Thuộc tính khái nim cho phép truy cập trạng thái của lớp thay thông qua
truy cập trực tiếp c biến thành viên, sẽ đựơc thay thế bằng việc thực thi truy cập
thông qua phương thức của lớp.
Đây thật sự một điều tưởng. Các thành phần bên ngoài (client) muốn truy cập
trạng thái của một đi tượng không muốn làm việc với nhng phương thức. Tuy
nhiên, người thiết kế lớp muốn dấu trạng thái bên trong của lớp mà anh ta xây dựng,
cung cấp một cách gián tiếp thông qua mt phương thức. Thuộc tính một đặc tính mới
{
int tg = a[0];
for (int i = 1; i < n; ++i)
tg = Max(tg, a[i]);
return tg;
}
}
public class Tester
{
static void Main()
{
TimMax m = new TimMax();
m.KhoiTao(); Console.WriteLine("Max cua day la:{0}", m.Max());
m.KhoiTao(5); Console.WriteLine("Max cua day la:{0}", m.Max());
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 45
được giới thiệu trong ngôn ngữ C#. Đặc tính này cung cấp khả năng bảo vệ các trường dữ
liệu bên trong mt lớp bằng việc đọc viết chúng thông qua thuộc tính. Trong ngôn ngữ
khác, điều này thể được thực hin thông qua việc tạo các phương thức lấy dữ liệu
(getter method) và phương thức thiết lập dữ liệu (setter method).
Thuộc tính được thiết kế nhắm vào hai mục đích: cung cấp một giao diện đơn cho
phép truy cập các biến thành viên, Tuy nhiên cách thức thực thi truy cập giống như
phương thức trong đó các dữ liệu được che dấu, đảm bảo cho yêu cầu thiết kế hướng đối
tượng. Để hiểu rõ đặc tính này ta sẽ xem dụ 2.1.7 bên dưới:
dụ 2.1.7: Sử dụng thuộc tính.
using System;
public class Time
{
public void DisplayCurrentTime()
{
Console.WriteLine("Time\t: {0}/{1}/{2} {3}:{4}:{5}",date, month, year, hour,
minute, second);
}
public Time( System.DateTime dt)
{
year = dt.Year;
month = dt.Month;
date = dt.Day;
hour = dt.Hour;
minute = dt.Minute;
second = dt.Second;
}
public int Hour
{
get
{
return hour;
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 46
Kết quả:
Time : 31/1/2007 17:55:1
Retrieved the hour: 17
Updated the hour: 18
set
{
hour = value;
}
}
// Biến tnh viên private
private int year;
private int month;
private int date;
private int hour;
private int minute;
private int second;
}
public class Tester
{
static void Main()
{
DateTime currentTime = System.DateTime.Now;
Time t = new Time( currentTime );
t.DisplayCurrentTime();
// Lấy dữ liệu từ thuộc tính Hour
int theHour = t.Hour;
Console.WriteLine("Retrieved the hour: {0}", theHour);
theHour++;
t.Hour = theHour;
Console.WriteLine("Updated the hour: {0}", theHour);
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 47
Để khai báo thuộc tính, đầu tiên khai báo tên thuộc tính để truy cập, tiếp theo là
phần thân định nghĩa thuộc tính nằm trong cập dấu ({}). Bên trong thân của thuộc tính là
khai o hai bộ truy cập ly và thiết lập dữ liệu:
Mỗi bộ truy cập được khai báo riêng biệt để làm hai công việc kc nhau lấy
hay thiết lập giá trị cho thuộc tính. Giá trị thuộc tính thể được lưu trong sở dữ liệu,
khi đó trong phần thân của bộ truy cập sẽ thực hiện các ng việc tương tác với sdữ
lịêu. Hoặc gtrị thuộc tính được lưu trữ trong các biến tnh viên của lớp như trong
dụ: private int hour;
Truy cập lấy dữ liệu (get accessor)
Phần khai báo tương tự như mt phương thức của lớp ng để trả về mt đối
tượng kiểu dữ liệu của thuộc tính. Trong dụ trên, b truy cập ly d liệu get của
thuộc tính Hour cũng tương tự như mt phương thức trả về một giá trị int. trả về giá
trị của biến thành viên hour nơi mà giá trị của thuộc tính Hour lưu trữ:
Trong dụ này, mt biến thành viên cục bộ được trvề, nhưng cũng th truy cập
dễ dàng mt giá trị nguyên tsở dữ lịêu, hay thực hiện việc tính toán tùy ý. Bất cứ khi
o chúng ta tham chiếu đến mt thuộc tính hay gán giá trị thuộc tính cho mt biến t
bộ truy cập lấy dữ liu get sẽ được thực hiện để đọc giá trị của thuộc tính:
public int Hour
{
get
{
return hour;
}
set
{
hour = value;
}
}
get
{ return hour; }
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 48
Khi lệnh thhai được thực hiện t giá trị của thuộc tính sẽ được trả về, tức là bộ truy cập
lấy d lịêu get sẽ được thực hiện kết quả gtrị của thuộc tính được n cho biến
cục bộ theHour.
Bộ truy cập thiết lập dữ liệu ( set accessor)
Bộ truy cập này sẽ thiết lập mt giá trị mới cho thuộc tính tương t như mt
phương thức trả về mt giá trị void. Khi định nghĩa bộ truy cập thiết lập dữ lịêu chúng ta
phải sử dụng tkhóa value để đại diện cho tham số được truyn vào được lưu trữ bởi
thuộc tính:
Như đã i trước, do ta đang khai o thuộc tính lưu tr dưới dạng biến thành viên
nên trong phần thân của b truy cập ta chỉ sử dụng biến thành viên mà thôi. Bộ truy cập
thiết lập hoàn toàn cho phép chúng ta thể viết giá tr vào trong sở dữ lịêu hay cập
nhật bất cứ biến thành viên nào khác của lớp nếu cần thiết.
Khi chúng ta gán một giá trị cho thuộc tính thì bộ truy cập thiết lập dữ liệu set sẽ
được t động thực hiện mt tham số ngầm đnh sẽ được tạo ra để lưu gtrị ta
muốn gán:
Lợi ích của hướng tiếp cận này cho phép các thành phần bên ngoài (client) thtương
tác với thuộc tính mt cách trực tiếp, mà không phi hy sinh việc che dấu dữ lịêu ng
như đặc tính đóng gói dữ lịêu trong thiết kế hướng đối tượng.
2.1.8. Từ khoá this
Như chúng ta biết rằng trong lập trình hướng đối tượng thì bài toán được chia
thành các đối tượng. Trong mi đối tượng các thành phần dữ liệu các phương thức.
Time t = new Time( currentTime );
int theHour = t.Hour;
set
{
hour = value;
}
theHour++;
t.Hour = theHour;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 49
Khi i tới các thành phần dữ liệu và các phương thức t phải gắn nó với mt đối tượng
cụ thể. Ta xem lại lớp Point trong phn trước:
Ví dụ: Trong lớp Point trong các phần trước ta
Thoạt nhìn ta thấy trong phương thức Init của lớp Point các thành phần dữ liệu
được sử dụng không gắn vi đối tượng cụ thể nào. Ta thấy vẻ không p hợp vi
nguyên tắc của lập tnh hướng đối tượng. Nhưng thực tế không phải thế. Trong C# sử
dụng mt đi đặc biệt this thong các phương thức(đối này một đối ngầm định). Các
thuộc tính viết trong phương thức được hiểu là thuộc một đi tượng của this. Ta thể
viết lại phương thức trên như sau:
public void Init(int ox, int oy)
{
this.x=ox;
this.y=oy;
}
Từ đó ta thấy rằng: Phương thức bao giờ ng ít nhất mt đi this luôn
đối đầu tiên của phương thức.
Xét một lời gọi tới phương thức Init(...)
Point A=new Point();
A.Init(3,4);
Trong trường hợp này tham số truyn cho this chính đối tượng A.
Do đó: this.x chính A.x...
Tóm lại: Tham số truyn cho đối this chính đối tượng đi kèm với phương thức trong
lời gọi phương thức.
dụ 2.1.8: Xây dựng chương trình tính tổng hai phân số
public void Init(int ox, int oy)
{ x=ox;
y=oy;
}
using System;
public class PS
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 50
{
private int tuso;
private int mauso;
public void nhap()
{
Console.Write("Nhap tu so:"); tuso = Convert.ToInt16(Console.ReadLine());
Console.Write("Nhap mau so:"); mauso = Convert.ToInt16(Console.ReadLine());
}
public void hien()
{
this.rutgon();
if (mauso == 1)
Console.WriteLine("{0}", tuso);
else
Console.WriteLine("{0}/{1}", tuso, mauso);
}
public int uscln(int x, int y)
{
x = Math.Abs(x);
y = Math.Abs(y);
while (x != y)
{
if (x > y) x -= y;
if (y > x) y -= x;
}
return x;
}
public void rutgon()
{
int uc = uscln(tuso, mauso);
tuso = tuso / uc;
mauso = mauso / uc;
if(tuso*mauso>0)
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 51
tuso=Math.Abs(tuso);mauso=Math.Abs(mauso);
}
else if(mauso<0)
{
tuso=-1*tuso;
mauso= Math.Abs(mauso);
}
}
public PS tong(PS b)
{
PS tmp=new PS();
tmp.tuso = this.tuso*b.mauso+this.mauso*b.tuso;
tmp.mauso = this.mauso*b.mauso;
tmp.rutgon();
return tmp;
}
}
public class Tester
{
static void Main()
{
PS a = new PS();
PS b = new PS();
a.nhap();
b.nhap();
a.tong(b).hien();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 52
Bài 3: Lp đi tưng( 2)
3.1. Phép gán c đối tưng
Như chúng ta đã biết trong C# hai loi kiểu dữ liệu đó là kiểu dữ liệu giá trị kiểu
dữ liu tham chiếu: Các kiểu dữ liệu g trị đó các kiểu dữ liệu xây dựng sẵn của C#
như: int, float,char... trkiểu dữ liệu xâu. c kiểu dữ liệu tham chiếu đó các kiểu dữ
liệu do ngườing định nghĩa như: đối tượng, mảng,...trừ kiểu dữ liệu cấu trúc.
Đối với kiểu dữ liệu giá tr t sẽ được lưu kích thước thật trong bộ nhđã cấp phát
stack. Trong khi đó thì địa chỉ của kiểu dữ liệu tham chiếu t được lưu trong stack nhưng
đối tượng thực sự thì lưu trong bộ nhớ heap
Chính vậy khi ta gán hai đối tượng cho nhau tđiều sẽ din ra.
Xét dụ sau: Trong phân trên ta xây dựng lớp Point ta có thể khai báo như sau:
Point a=new Point(); a.Dispaly();
Point b=new Point(); b.Init(6,3);
a=b;
Ta thể hình điều này như sau:
Ta nhận thấy rằng sau khi n đối tượng b vào đối tượng a t ta thấy rằng hai đối tượng
a, b cùng sử dụng chung vùng nhớ. Điều này pvnguyên tắc của lập trình hướng đối
tượng.
3.2. Phương thc thiết lập sao chép
3.2.1. Phương thức thiết lập
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 53
Đặc điểm của phương thức thiết lập
Thử xem li dụ minh họa 2.1b, câu lệnh tạo mt đối tượng cho lớp ThoiGian
tương tự nviệc gọi thực hiện mt phương thức:
ThoiGian t = new ThoiGian();
Đúng như vậy, mt phương thức sẽ được gọi thực hiện khi chúng ta tạo mt đối tượng.
Phương thức này được gọi là bộ khởi dựng hay phương thức thiết lập (constructor). Các
phương thức này được định nghĩa khi xây dựng lớp, nếu ta không tạo ra t CLR sẽ thay
mặt chúng ta mà tạo phương thức khi dựng mt cách mặc định. Chức năng của bộ khi
dựng to ra đối tượng được xác định bi mt lớp đặt trạng thái này hợp lệ. Trước
khi b khởi dựng được thực hiện t đối tượng chưa được cấp phát trong bộ nhớ. Sau khi
bộ khởi dựng thực hiện hoàn thành thì bộ nhớ sẽ lưu gimột thể hin hợp lcủa lớp vừa
khai o.
Lớp ThoiGian trong dụ 2.1b không định nghĩa bộ khởi dựng. Do không định
nghĩa nên trình biên dch sẽ cung cấp mt bkhi dựng cho chúng ta. Phương thức khởi
dựng mc định được tạo ra cho một đối tượng sẽ không thực hiện bất cứ nh động nào,
tức là bên trong thân của phương thức rỗng. Các biến thành viên được khởi tạo các giá trị
tầm thường như thuộc tính nguyên giá tr là 0 chuỗi t khởi tạo rỗng,..Bảng 2.3.1
sau tóm tt các giá trị mặc định được gán cho các kiểu dữ liệu cơ bản.
Kiểu dữ liệu
Giá trị mặc định
int, long, byte,
0
bool
false
char
\0’ (null)
enum
0
reference
null
Bảng 2.3.1: Giá trị mặc định của kiểu dữ liệu bản.
Thường thường, khi muốn định nghĩa mt phương thức khởi dng riêng ta phải cung cấp
các tham số để hàm khởi dựng thể khởi to các giá trị khác ngoài giá trị mc định cho
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 54
các đối tượng. Quay lại dụ 2.1b giả sử ta muốn truyn thời gian hiện hành: năm, tháng,
ngày,…để đối tượng ý nghĩa hơn.
Một phương thức thiết lập được định nghĩa tường minh các đặc điểm sau:
1. Phương thức thiết lập cùng tên với tên của lớp.
2. Phương thức thiết lập thường thuộc tính public.
3. Phương thức thiết lập không giá trị trả về không cần khai báo void.
4. Có thể có nhiều phương thức thiết lập trong cùng lớp (chồng các phương thức
thiết lập).
5. Khi mt lớp có nhiều phương thức thiết lập, việc tạo các đối tượng phi kèm
theo các tham số phù hợp với một trong các hàm thiết lập đã khai báo.
dụ 2.3.1: Định nghĩa mt bộ khởi dựng.
using System;
public class ThoiGian
{
public void ThoiGianHienHanh()
{
Console.WriteLine( Thoi gian hien hanh la : {0}/{1}/{2} {3}:{4}:{5}”, Ngay,
Thang, Nam, Gio, Phut, Giay);
}
// Phương thức thiết lập
public ThoiGian( DateTime dt )
{ Nam = dt.Year;
Thang = dt.Month;
Ngay = dt.Day;
Gio = dt.Hour;
Phut = dt.Minute;
Giay = dt.Second;
}
// Biến tnh viên private
int Nam;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 55
Kết quả:
Thoi gian hien hanh la: 01/02/2007 9:10:20
Trong dụ trên phương thức khởi dng ly mt đối tượng DateTime và khởi tạo
tt cả các biến thành viên dựa trên giá trị của đối tượng này. Khi phương thức này thực
hiện xong, một đi tượng ThoiGian được tạo ra c biến của đối tượng cũng đã được
khởi tạo. Hàm ThoiGianHienHanh được gọi trong hàm Main() sẽ hiển thị giá trị thời gian
lúc đối tượng được tạo ra.
Chúng ta th bỏ một số lệnh khởi tạo trong phương thức khởi dựng cho thực
hiện chương trình lại thì c biến không được khởi to sẽ có giá trị mặc định là 0, do là
biến nguyên. Một biến thành viên kiểu nguyên sđược thiết lập giá trị 0 nếu chúng ta
không n trong phương thức khi dựng. Chú ý rằng kiểu dữ liệu giá trị không thể
không được khởi tạo, nếu ta không khởi tạo thì trình biên dịch sẽ cung cấp các gtrị mặc
định theo bảng 2.3.1
int Thang;
int Ngay;
int Gio;
int Phut;
int Giay;
}
public class Tester
{
static void Main()
{
System.DateTime currentTime = System.DateTime.Now;
ThoiGian t = new ThoiGian( currentTime );
t.ThoiGianHienHanh();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 56
Ngoài ra trong chương trình 2.3.1 trên sử dụng đối tượng của lớp DateTime,
lớp DateTime này được cung cấp bi thư viện System, lớp này ng cung cấp các biến
thành viên public như: Year, Month, Day, Hour, Minute, Second tương tự như lớp
ThoiGian của chúng ta.
Thêm vào đó là lớp này đưa ra một phương thức thành viên tĩnh tên là Now,
phương thức Now sẽ tr về mt tham chiếu đến mt thể hin của mt đối tượng
DateTime được khi tạo với thời gian hiện hành.
Theo như trên khi lệnh :
System.DataTime currentTime = System.DateTime.Now();
được thực hiện t phương thức tĩnh Now() sẽ to ra mt đối tượng DateTime trên bộ nhớ
heap trả về một tham chiếu tham chiếu này được n cho biến đối tượng
currentTime.
Sau khi đối tượng currentTime được tạo t câu lệnh tiếp theo s thực hin việc
truyền đối tượng currentTime cho phương thức khởi dựng để tạo mt đối tượng
ThoiGian:
ThoiGian t = new ThoiGian( currentTime );
Bên trong phương thức khởi dựng này tham số dt sẽ tham chiếu đến đối tượng DateTime
đối tượng vừa to mà currentTime ng tham chiếu. Nói cách khác lúc này tham số dt
currentTime cùng tham chiếu đến một đối tượng DateTime trong bộ nhớ. Nhờ vậy
phương thức khởi dựng ThoiGian thể truy cập được các biến thành viên public của đối
tượng
DateTime được tạo trong hàm Main().
một sự nhn mạnh đây đối tượng DateTime được truyn cho bộ dựng
ThoiGian chính đối tượng đã được tạo trong hàm Main là kiểu dữ liệu tham chiếu.
Do vậy khi thực hiện truyn tham số là một kiểu dữ liệu tham chiếu t con trỏ được ánh
xạ qua chhoàn toàn không có một đối tượng nào được sao chép lại.
Sử dụng phương thức thiết lập
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 57
Như chúng ta đã biết trong mt khai báo lớp nếu chúng ta không tạo ra phương
thức thiết lập tường minh thì chương trình dịch tự tạo ra mt phương ththiết lập không
tham số mi khi một đối tượng được tạo ra.
dụ trong dụ trước ta lớp Point khi đó
Point a=new Point();// t một phương thức thiết lập không tham số t
động được to ra khi khai báo đi tượng a
Xét ví dụ sau:
using System;
public class Point
{
int x,y;
public Point(int ox, int oy)
{
x = ox;
y = oy;
}
public void Move(int dx, int dy)
{
x += dx;
y += dy;
}
public void Display()
{
Console.WriteLine("Toa do la:({0},{1})",x,y);
}
}
public class Tester
{
static void Main()
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 58
Để câu lnh trên đúng t hoặc chúng ta bỏ phương thức thiết lập hai tham số trong lp
Point hoặc thêm mt phương thức thiết lập không tham số o lớp Point hoặc đưa tm
tham số vào câu lệnh trên
Point []a = new Point[5];
// Khai o mảng đối tượng con trỏ
// Cấp phát bộ nhớ cho từng phần tử của mảng
for (int i = 0; i < a.Length; ++i)
a[i] = new Point();
// Câu lệnh này không hợp lệ do không phương
thức thiết lập phù hợp khi tạo ra đối tượng
for (int i = 0; i < a.Length; ++i)
a[i].Display();
}
}
using System;
public class Point
{
int x,y;
public Point(int ox, int oy)
{
x = ox;
y = oy;
}
public Point()
{
x = 0;
y = 0;
}
public void Move(int dx, int dy)
{
x += dx;
y += dy;
}
int y;
public void Display()
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 59
3.2.2. Phương thức sao chép
Bộ khi dựng sao chép thực hiện việc tạo mt đi tượng mi bằng cách sao chép
tt cả các biến từ một đối tượng đã cùng mt kiểu dữ liệu. dụ chúng ta muốn
đưa một đối tượng PhanSo vào b khởi dựng lp PhanSo để tạo một đối tượng PhanSo
mới cùng giá trị với đối tượng PhanSo cũ. Hai đối tượng này hoàn toàn khác nhau và
chỉ ging nhau ở giá tr biến thành viên sau khi khởi dựng.
Ngôn ngữ C# không cung cấp bộ khởi dựng sao chép, do đó chúng ta phải t tạo
ra. Việc sao chép các thành phần từ mt đối tượng ban đầu cho mt đối tượng mới như
sau:
public ClassName(ClassName b)
{
// sao chép các giá trị của thành phần đối tượng b cho lớp.
}
dụ:
Console.WriteLine("Toa do la:({0},{1})",x,y);
}
}
public class Tester
{
static void Main()
{
Point []a = new Point[5]; // Khai báo mng đối tượng con tr
a[0] = new Point(2,3); // Phương thức thiết lập hai tham số được gọi
// Cấp phát bộ nhớ cho từng phần tử của mảng
for (int i = 1; i < a.Length; ++i)
a[i] = new Point(); // Câu lnh này đúng vì lúc này phương thức thiết
lập không tham số được gọi
for (int i = 0; i < a.Length; ++i)
a[i].Display();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 60
public PhanSo(PhanSo t)
{
this.ts = t.ts;
this.ms = t.ms;
}
Khi đó ta có thể sao chép t mt đối tượng PhanSo đã hiện hữu như sau:
PhanSo a = new PhanSo(n, m);
PhanSo b = new PhanSo(a);
Trong đó a là đối tượng PhanSo đã tồn tại, sau khi lệnh trên thực hin xong t đối tượng
b được tạo ra như bn sao của đi tượng a.
3.3. Hu bỏ đối tưng, chế gom rác trong .Net
Ngôn ngữ C# cung cấp chế thu dọn (garbage collection) 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 đoạn
không được quản t 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 mt 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 b kết thúc. Phương thức Finalize
này sẽ được gọi bởi cơ chế thu dọn khi đối tượng bị hủy.
Phương thức kết thúc chỉ giải phóng các tài nguyên đối tượng nm giữ,
không tham chiếu đến các đối tượng khác. Nếu vi những đoạn mã nh thường tức là
chứa c tham chiếu kiểm soát được t không cần thiết phải tạo thực thi phương thức
Finalize(). Chúng ta chỉ làm điều này khi xử các tài nguyên không kiểm soát được.
Chúng ta không bao gi gọi mt phương thức Finalize() của mt đối tượng mt
cách trực tiếp, ngoại trừ gọi phương thức này của lớp cơ sở khi n trong phương thức
Finalize() của chúng ta. Trình thu dọn sẽ thực hiện việc gọi Finalize() cho chúng ta.
Cách Finalize thực hiện: B thu dọn duy trì mt danh sách những đối tượng phương
thức Finalize. Danh sách y được cập nhật mi ln khi đối tượng cuối cùng được to ra
hay bị hy. Khi mt đối tượng trong danh sách kết thúc của bộ thu dọn được chọn đầu
tiên. sẽ được đặt vào hàng đợi (queue) cùng vi những đối tượng khác đang chờ kết
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 61
thúc. Sau khi phương thức
Finalize của đối tượng thực thi bộ thu dọn sẽ gom li đố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.
Bộ hủy của C#: pháp phương thức hủy trong nn ngữ C# cũng ging như trong
ngôn ngữ C++.
Nhưng về hành động cụ thể chúng nhiều điểm khác nhau. Ta khai báo một
phương thức hủy trong C# như sau:
~Class1() {}
Tuy nhiên, trong nn ngữ C# t pháp khai báo trên mt shortcut liên kết đến mt
phương thức kết thúc Finalize được liên kết với lớp cơ sở, do vậy khi viết
~Class1()
{
// Thực hin mt số công việc
}
Cũng tương tự như viết :
Class1.Finalize()
{
// Thực hiện mt số công việc
base.Finalize();
}
Do sự tương tnhư trên nên khả năng dẫn đến sự lộn xộn nhầm ln không tránh khỏi,
nên chúng ta phải tránh viết các phương thức hủy viết các phương thức Finalize tường
minh nếu có thể được.
Phương thức Dispose: Như chúng ta đã biết thì việc gọi mt phương thức kết thúc
Finalize trong C# không hợp lệ, phương thức này dành cho bộ thu dọn thực hin.
Nếu chúng ta xử các tài nguyên không kiểm soát như xử c handle của tập tin ta
muốn được đóng hay giải phóng nhanh chóng bất cứ lúc nào, ta thực thi giao din
IDisposable, phần chi tiết IDisposable sẽ được tnh bày chi tiết trong Chương sau. Giao
diện IDisposable u cầu những thành phần thực thi của định nghĩa mt phương thức
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 62
tên Dispose() để thực hiện ng việc dọn dẹp ta yêu cầu. Ý nghĩa của phương thức
Dispose là cho phép chương trình thực hiện 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 gi
phương thức Finalize() trong đối tượng của chúng ta. Để ngưng b thu dọn, chúng ta gi
mt phương thức tĩnh của lớp GC (garbage collector) là GC.SuppressFinalize() và truyn
tham số là tham chiếu this của đối tượng. 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ọc GC trong thực hiện kết thúc
GC.SuppressFinalize( this );
}
public override void Finalize()
{
Dispose();
base.Finalize();
}
Phương thức Close: Khi xây dựng c đối tượng, chúng ta muốn cung cấp cho người
sử dụng phương thức Close(), phương thức Close v tự nhiên hơn phương thức
Dispose trong các đối tượng 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 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().
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 sdụng thể gọi hàm Dispose(). cũng không kim soát được lúc nào t bộ
thu dọn GC thực hin việc dọn dẹp. Do đó để cung cấp khả năng mạnh hơn để kim soát
việc giải phóng tài nguyên thì C# đưa ra pháp chỉ dẫn using, pháp này đảm bảo
phương thức Dispose() sẽ được gọi sớm nhất thể được. Ý tưởng là khai báo các đối
tượng với pháp using sau đó to một phạm vi hoạt động cho các đối tượng này
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 63
trong khối được bao bi dấu ({}). Khi khối phm vi này kết thúc, thì phương thức
Dispose() của đối tượng sẽ được gọi mt cách tự động.
dụ 2.4: Sử dụng chỉ dẫn using.
Trong phần khai báo đầu của dụ t đối tượng Font được khai báo n trong câu
lệnh using. Khi câu lệnh using kết thúc, t phương thức Dispose của đối tượng Font sẽ
được gọi.
Còn trong phần khai báo thứ hai, mt đối tượng Font được tạo bên ngoài câu lnh
using. Khi quyết định dùng đối tượng này ta đặt o câu lnh using. cũng tương t
như trên khi khối câu lnh using thực hiện xong thì phương thức Dispose() của font được
gọi.
using System.Drawing;
class Tester
{
public static void Main()
{
using ( Font Afont = new Font(“Arial”,10.0f))
{
// Đoạn sử dụng AFont. ......
} // Trình biên dịch sẽ gọi Dispose để gii phóng Afont
Font TFont = new Font(“Tahoma”,12.0f);
using (TFont)
{
// Đoạn sử dụng Tfont .......
}// Trình biên dịch gọi Dispose để giải phóng TFont
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 64
Bài 4: Lp đi tưng( 3)
4.1. Thành phn tĩnh cách sử dụng
4.1.1. Thành phần dữ liệu nh
Những thuộc tính và phương thức trong mt lớp thể là những thành viên th
hiện (instance members) hay những thành viên tĩnh (static members). Nhng thành viên
thhiệ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ư mt phần của lớp. Chúng ta thtruy cập đến tnh
viên tĩnh của mt lớp thông qua tên lớp đã được khai báo
Ghi chú: Trong ngôn ngữ C# không cho phép truy cập đến các phương thức tĩnh
các biến thành viên tĩnh thông qua mt thể hiện, nếu chúng ta cố làm điều đó t trình
biên dch C# sẽ báo li, điều này khác với nn ngữ C++.
Trong mt số ngôn ngữ thì sự phân chia giữa phương thức của lớp và các
phương thức khác (toàn cục) tồn tại bên ngoài không phụ thuộc bất cứ một lớp o. Tuy
nhiên, điều này không cho phép trong C#, ngôn ngữ C# không cho phép tạo các phương
thức bên ngoài của lớp, nhưng ta thể to được các phương thức ging như vậy bằng
cách tạo các phương thức tĩnh bên trong một lớp. 5.1.1 Thành phần dữ liệu tĩnh
Sử dụng các thuộc nh nh
Như ta đã biết khi i tới mt thành phần dữ liệu t ta phải nghĩ ngay đến gắn
với mt đối tượng cụ thể o đó. Trong qua trình lập trình ta muốn những thành phần
dữ liệu không thuộc bất kỳ đối tượng nào. Để được điều đó t thành phần dữ liệu đó
phải là tĩnh bằng cách đặt tkhóa static trước tên thành phần dữ liệu đó. Như vậy mt
thành phn dữ liệu tĩnh thì được cấp phát một vùng nhớ cố định nó không phi là
riêng của một đi tượng nào.
Để truy nhập tới một thành phần dữ liệu tĩnh ta dùng tên lớp không được truy
nhập thông qua tên mt đối tượng. Chính vy các tnh phần dữ liệu tĩnh chỉ được sử
dụng trong các phương thức tĩnh.
dụ 2.5b: Nhập vào một danh ch cán bộ cho biết tổng lương của các cán b vừa
nhp
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 65
using System;
public class Canbo
{
string hoten;
double luong;
public static double tl=0;
public void nhap()
{
Console.Write("Ho ten:"); hoten = Console.ReadLine();
Console.Write("Luong:"); luong = Convert.ToDouble(Console.ReadLine());
tl = tl + luong;
}
public void hien(int i)
{
Console.WriteLine("{0}\t{1}\t{2}", i, hoten, luong);
}
}
public class DsCanBo
{
int n;
Canbo[] ds;
public void nhap()
{
Console.Write("Nhap so can bo:"); n = Convert.ToInt16(Console.ReadLine());
ds=new Canbo[n];
for (int i = 0; i < n; ++i) ds[i] = new Canbo();
Console.WriteLine("Nhap thong tin cho cac can bo");
for (int i = 0; i < n; ++i) ds[i].nhap();
}
public void hien()
{
for (int i = 0; i < n; ++i) ds[i].hien(i + 1);
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 66
4.1.2. Phương thức nh
Phương thức tĩnh hoạt đng ít nhiều ging như phương thức toàn cục, ta truy cập
phương thức y mà không cần phải tạo bất cứ thể hiện hay đối tượng của lớp chứa
phương thức toàn cục. Tuy nhiên, li ích của phương thức tĩnh vượt xa phương thức toàn
cục phương thức tĩnh được bao bọc trong phạm vi của mt lớp nơi được định nghĩa,
do vậy ta sẽ không gặp tình trạng ln xộn giữa các phương thức trùng tên do chúng được
đặt trong namespace.
Ghi chú: Chúng ta không nên bị cám dỗ bởi việc to ra mt lớp chứa toàn bộ các
phương thức linh tinh. Điều này thể tin cho công việc lập trình nhưng sẽ điều không
mong muốn giảm tính ý nghĩa của việc thiết kế hướng đối tượng. Vì đặc tính của việc
to các đối tượng xây dựng các phương thức hành vi xung quanh các thuộc tính hay
dữ liu của đối tượng.
Gọi một phương thức tĩnh:
Như chúng ta đã biết phương thức Main() mt phương thức tĩnh. Phương tĩnh
được xem như là phần hoạt động của lớp hơn là của thể hiện mt lớp. Chúng cũng không
cần mt tham chiếu this hay bất cứ thể hiện nào tham chiếu tới. Như vậy phương thức
tĩnh không đối ngầm địh là this. Do vậy phương thức tĩnh không thtruy cập trực tiếp
đến các thành viên không tính chất tĩnh (nonstatic). Như vy Main() không thể gi
mt phương thức không tĩnh bên trong lớp.
}
public class Tester
{
static void Main()
{
DsCanBo c = new DsCanBo();
c.nhap();
Console.WriteLine("\t\t\tDanh sach cac can bo la\n");
Console.WriteLine("STT\tHo va ten\tLuong");
c.hien();
Console.WriteLine("Tong luong cua cac can bo la:{0}", Canbo.tl);
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 67
Để xây dựng mt phương thức tĩnh ta thêm từ khóa static trong định nghĩ
phương thức.
dụ 2.5a: Cho P(x)=a[0]x
n
+a[1]x
n-1
+..+a[n]
nh
P( x)
3
using System;
public class DaThuc
{
int n;
double x;
double [] hs = new double [50];
public void nhap()
{
Console.Write("Nhap he so=");
n = Convert.ToInt16(Console.ReadLine());
Console.Write("Nhap x="); x = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Nhap cac he so");
for (int i = 0; i <=n; ++i)
{
Console.Write("hs[{0}]=", i);
hs[i] = Convert.ToDouble(Console.ReadLine());
}
}
public static double mu(double x, int n)
{
if (n == 0) return 1;
else return x * mu(x, n - 1);
}
public static double can3(double x)
{
double y;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 68
Như vậy phương thức tĩnh là phương thức chung cho tt cả các đối tượng của lớp,
không thuộc bất kỳ đối tượng nào. Ta thể coi phương thức tĩnh như là mt phương
thức toàn cục của lớp, các phương thức tình chỉ dùng để xử các thành phần tĩnh
dụ trong phương thức can3 ta viết như sau là sai
if (x == 0) y = 0;
else if (x > 0) y = Math.Exp(1.0 / 3 * Math.Log(x));
else y = -Math.Exp(1.0 / 3 * Math.Log(-x));
return x;
}
public double gtdt()
{
double p = 0;
for (int i = 0; i <=n; ++i)
p = p+hs[i]*mu(x,n-i);
return p;
}
}
public class Tester
{
static void Main()
{
DaThuc p = new DaThuc();
p.nhap();
Console.WriteLine("Gia tri cua da thuc ={0}", DaThuc.can3(p.gtdt()/3));
}
}
public static double can3(double x)
{
double y;
n=10;
/* Sai vi n không phải thuộc tính tĩnh. Nếu viết như này t chương trình dịch hiểu :
this.n=10. Nhưng trong phương thức tĩnh thì không có đối ngầm định là this*/
if (x == 0) y = 0;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 69
4.2. Lp bao ch sử dụng
Những lớp lồng bên trong có li là có khả năng truy cập đến tt cả các thành viên của lớp
ngoài.
Một phương thức của lớp lng thể truy cập đến biến thành viên private của lớp ngoài.
Hơn nữa, lớp lồng bên trong có thể ẩn đối với tất ccác lớp khác, lớp lng có thể
private cho lớp ngoài.
Cuối cùng, mt lớp làm lng bên trong là public và được truy cập bên trong phạm
vi của lớp ngoài. Nếu mt lớp Outer là lớp ngoài, và lớp Nested là lớp public lng bên
trong lớp Outer, chúng ta có thể tham chiếu đến lp Tested như Outer.Nested, khi đó lớp
bên ngoài hành động ít nhiều ging như một namespace hay mt phạm vi.
else if (x > 0) y = Math.Exp(1.0 / 3 * Math.Log(x));
else y = -Math.Exp(1.0 / 3 * Math.Log(-x));
return x;
}
class Class1
{
private int a,b;
public Class1 (int a, int b)
{
this.a = a;
this.b = b;
}
public class con
{
public void tong(Class1 l)
{
System.Console.WriteLine("day la lop long ben trong ");
System.Console.WriteLine("a {0} + b {1} = {2}", l.a, l.b, l.a + l.b);
}
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 70
Điều thú vị trong phương thức Tong() truy cập dữ liệu thành viên private l.a
l.b. Hai viến thành viên private này sẽ không cho phép truy cập nếu con không phải
lớp lồng bên trong của lớp Class1
Lưu ý trong hàm Main() khi khai báo mt thể hiện của lớp lồng bên trong, chúng
ta phải xác nhận tên của lớp bên ngoài, tức là lớp Class1.
Class1 t1 = new Class1(4, 5);
Class1.con t2 = new Class1.con();
t2.tong(t1);
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 71
Bài 5: Kế tha đa hình(1)
5.1. Gii thiu chung về tha kế
Thừa kế là một trong bn nguyên tắc s của phương pháp lập trình hướng đối
tượng. Đặc biệt đây sở cho việc ng cao kh năng sử dụng lại các bộ phận của
chương trình. Thừa kế cho phép ta định nghĩa một lớp mi, gọi lớp dẫn xuất, từ mt
lớp đã , gọi lớp sở. Lớp dẫn xuất sẽ thừa kế các thành phần (dữ liệu, hàm) của lớp
sở, đồng thời thêm vào các thành phần mới, bao hàm c việc làm “tốt n” hoặc làm
lại những ng việc trong lớp sở chưa làm tốt hoặc không n phù hợp với lớp dẫn
xuất. Chẳng hạn thể định nghĩa lớp “mặt hàng nhập khẩu” dựa trên lớp “mặt hàng”,
bằng cách bổ sung thêm thuộc tính “thuế”. Khi đó cách tính chênh lệch giá bán, mua
trong lớp “mặt hàng” sẽ không phợp nữa nên cần phải sửa lại cho phù hợp. Lớp điểm
màu được đnh nghĩa dựa trên lớp điểm không màu bằng cách bổ sung thêm thuộc tính
u, hàm display() c y ngoài việc hiển thị hai thành phần toạ độ còn phải cho biết
u của đối tượng điểm. Trong cả hai dụ đưa ra, trong lớp dn xuất đều sự bổ sung
thay đổi thích hợp với tình hình mới.
Thừa kế cho phép không cần phải biên dịch li các thành phn chương trình vốn đã
trong các lớp shơn thế nữa không cần phải chương trình nguồn tương ng.
Kỹ thuật này cho phép chúng ta phát triển các công cụ mới dựa trên nhng gì đã có được.
Thừa kế ng cho phép nhiều lớp thể dẫn xuất từ cùng mt lớp sở, nhưng
không chỉ giới hn một mức: mt lớp dẫn xuất thể là lớp sở cho các lớp dẫn xuất
khác. đây ta thấy rằng khái niệm tha kế ging như ng cụ cho phép mô t cụ thể hoá
các khái niệm theo nghĩa: lớp dẫn xuất là mt cụ thể hoá hơn nữa của lớp sở nếu bỏ
đi các d biệt trong các lớp dẫn xuất sẽ chỉ còn các đặc đim chung nm trong lớp cơ sở.
Hình 4.1 mô tmột đồ thừa kế của các lớp, cung đi tlớp này sang lớp kia nếu
chúng quan hệ thừa kế. Ta gọi đó đồ thị thừa kế. Sau đây là mt số tả cho các
lớp xuất hiện trong đồ thị thừa kế ở trên.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 72
1. Lớp mặt hàng
các thuộc nh
n
số lượng trong kho
giá mua
giá bán
các phương thức
hàm chênh lệch gbán mua
{giá bán - giá mua}
thủ tục mua(q)
{Thêm vào trong kho q đơn vị mặt ng}
thủ tục bán(q)
{Bớt đi q đơn vị mặt ng trong kho}
2. Lớp mặt hàng nhập khẩu thừa kế từ mặt hàng
các thuộc nh
thuế nhập khu
các phương thức
hàm chênh lệch gbán -mua
{giá bán - giá mua* thuế nhập khu}
3. Lớp xe gắn máy thừa kế từ mặt hàng nhập khẩu
các thuộc nh
dung tích xy lanh
các phương thức
4. Lớp hàng đin tử dân dụng thừa kế từ mặt hàng
các thuộc nh
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 73
điện áp
thi hạn bảo hành
các phương thức
hàm thi gian bảo hành thực tế
...
Tính đa hình ng mt trong các đim ttrong lập trình hướng đối tượng, được
thiết lập trên sở thừa kế trong đó đối tượng thể biểu hiện khác nhau tuỳ thuộc
o tình huống cụ thể. Tính đa nh ấy thể xảy ra mt hành vi của đối tượng hay
trong toàn bộ đối tượng. dụ trực quan thhiện tính đa hình mt ti vi thể vừa là
đối tượng của mặt hàng vừa đối tượng của lớp mặt hàng điện t dân dụng. Các đối
tượng nh hc nhình vuông, nh tròn, hình chnhật đều cùng cách vnhư nhau:
c định hai điểm đầu cuối, nối hai điểm này. Do vy thuật toán tuy ging nhau đối
với tất cả các đối tượng hình, nhưng cách vẽ t phụ thuộc vào từng lớp đối tượng cụ thể.
Ta nói phương thức ni đim của các đối tượng hình học tính đa hình. Tính đa nh
còn được thhin trong cách thức hiển thị thông tin trong các đối tượng điểm u/không
màu.
Các ngôn ngữ lập trình hướng đối tượng đều cho phép đa thừa kế, theo đó mt lớp
thlà dẫn xuất của nhiều lớp khác. Do vậy dẫn tới khnăng mt lớp sở thể được
thừa kế nhiều lần trong mt lớp dẫn xuất khác, ta gọi đó là sự xung đột thừa kế. Điều y
hoàn toàn không hay, cần phải tránh. Từng ngôn ngữ sẽ nhng gii pháp của riêng
mình, C# đưa ra khái niệm thừa kế ảo.
Trong chương này ta sẽ đề cập tới các kh năng của C# để thể hin nguyên tắc thừa
kế khi viết chương trình.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 74
Lớp Tivi
các thuộc nh
kích thước màn hình
điều khiển từ xa
Lớp Đồ điện tử dân dụng
các thuộc tính
điện áp
thời hạn bảo hành
các phương thức
thời gian bảo hành thực tế
Lớp Ôtô
các thuộc nh
c
5.2. Xây dng lớp dn xut tha kế từ lp sở
Xây dựng lớp Luong để tính lương cho các n bộ với các thông tin sau:
Các thành phần dữ liệu
- String Hoten; // thuộc tình ng để nhập thông tin về họ tên cho Cấn bộ
- double HesSoLuong;// Là thuộc tính dùng để nhập thong tin về hệ số
lương cho cán b
- int LuongCoBan;// Là thuộc tính dùng để nhập lương cơ bản cho các cán
bộ(Biết rằng tất cả các cán bộ t chung lương cơ bn
Các phương thức
Lớp mặt hàng
các thuộc tính
n
số lượng trong kho
giá mua
giá bán
các phương thức
chênh lệch giá bán mua
mua(q)
bán(q)
Lớp mặt hàng nhập khẩu
các thuộc nh
thuế nhập khẩu
các phương thức
chênh lệch giá bán -mua
Lớp Xe gắn máy
các thuộc tính
dung tích xy lanh
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 75
- Phương thức khởi tạo không tham số dùng để khởi tạo giá trị lương cơ bản
LuongCoBan = 450, HeSoLuong=2.34;
- Phương thức thiết lập hai tham số dùng để khởi tạo LuongCoBan
HesoLuong thông qua đối số của phương thức
- Phương thức nhập thông tin của lớp
- Phương thức hiện thông tin của lp
- Phương thức tính lương theo công thức HeSoLuong*LuongCoBan
Sau đó kế thừa lớp Luong để xây dựng lớp LuongMoing để tính lương mới cho
các cán bộ với việc bổ xung hệ số ph cấp(HeSoPhuCap) cho mi cán bộ. Từ đó việc
tính lương được tính theo công thức:
(HeSoLuong*LuongCoBan)+(HeSoLuong*LuongCoBan)*HeSoPhuCap. Hãy bổ xung
các thành phần dữ liệu cũng như các phương thứ c cần thiết vào lớp LuongMoi để có thể
tính lương cho cán bộ theo chuẩn mới đã nêu.
Trong ngụn ngữ C# để to một lớp dẫn xuất từ một lớp ta thêm dấu hai chấm vào
sau tên lớp dẫn xuất và trước tên lớp cơ sở:
Xây dựng chương trình sử dụng các lớp trên
dụ 12.1 Xây dựng lớp sở lớp kế thừa
using System;
class Luong
{
private string Hoten;
private double HeSoLuong;
public static int LuongCoBan;
public Luong()
{
LuongCoBan = 450;
HeSoLuong = 2.34;
}
public Luong(int lcb,double hsl)
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 76
LuongCoBan = lcb;
HeSoLuong = hsl;
}
public void Nhap()
{
Console.Write("Ho va ten:");
Hoten = Console.ReadLine();
Console.Write("He so luong:");
HeSoLuong = int.Parse(Console.ReadLine());
}
public double TinhLuong()
{
return LuongCoBan * HeSoLuong;
}
public void Hien()
{
Console.WriteLine("Ho ten:{0}\nHe so luong:{1}", Hoten, HeSoLuong);
}
}
class LuongMoi : Luong
{
private double HeSoPhuCap;
public LuongMoi(): base()
{
HeSoPhuCap = 0.4;
}
public LuongMoi(int lcb, double hsl, double hspc): base(lcb, hsl)
{
HeSoPhuCap = hspc;
}
public new void Nhap()
{
base.Nhap();
Console.Write("He so phu cap:");
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 77
5.3. Mc truy cập trong lp dẫn xut
Kh năng hiện hữu của mt lớp các thành viên của th được hạn chế
thông qua việc sử dụng các bổ sung truy cập: public, private, protected, internal, và
protected internal.
Như chúng ta đã thấy, public cho phép mt thành viên thể được truy cập bởi
mt phương thức thành viên của những lớp khác. Trong khi đó private chỉ cho phép các
phương thức thành viên trong lớp đó truy xuất. Từ khóa protected t mở rộng thêm khả
năng của private cho phép truy xuất tcác lớp dẫn xuất của lớp đó. Internal mở rộng khả
năng cho phép bất cứ phương thức của lớp o trong cùng mt khối kết hợp (assembly)
thể truy xuất được. Một khi kết hợp được hiểu như là mt khối chia xẻ ng lại
trong CLR. Thông thường, khối này là tập hợp các tập tin vật được lưu trữ trong một
thư mc bao gồm các tập tin tài nguyên, chương trình thực thi theo ngôn ngữ IL,...
Từ khóa internal protected đi cùng với nhau cho phép các thành viên của cùng một
khối assembly hoặc các lớp dẫn xuất của nó có thể truy cập.
HeSoPhuCap = double.Parse(Console.ReadLine());
}
public new double TinhLuong()
{
return base.TinhLuong() + base.TinhLuong() * HeSoPhuCap;
}
}
class Tester
{
static void Main()
{
LuongMoi a = new LuongMoi();
a.Nhap();
a.Hien();
Console.WriteLine("Luong:{0}", a.TinhLuong());
Console.ReadKey();
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 78
Chúng ta thxem sự thiết kế này ging như internal hay protected. Các lớp
cũng như những thành viên của lớp thể được thiết kế với bất cứ mức độ truy xuất nào.
Một lớp thường mức độ truy xuất mrộng hơn cách thành viên của lớp, còn các thành
viên t mức độ truy xuất thường có nhiều hạn chế. Do đó, ta có thđịnh nghĩa mt lớp
MyClass như sau:
Như trên biến thành viên myValue được khai báo truy xuất protected mặc bản
thân lớp được khai báo public. Một lớp public mt lớp sẵn sàng cho bất cứ lớp nào
khác muốn tương tác với nó. Đôi khi mt lớp được tạo ra chỉ để trợ giúp cho nhng lớp
khác trong mt khối assemply, khi đó nhng lớp này nên được khai báo khóa internal
hơn là khóa public.
Dưới đây bảng về mức truy cập các thành phần của mt lp
Các thành phần trong
Lớp A
Lớp B kế thừa lớp A
Lớp C không kế thừa lớp
A
private
Không truy xuất được
Không truy xuất được
protected
Truy xuất được B dẫn xuất từ A
Không truy xuất được
internal
Truy xuất được nếu B(C) cùng khối kết hợp (assembly) với A
(cùng mt project)
protected internal
Truy xuất được B dẫn xuất từ A
Truy xuất được nếu C ng
khối kết hợp (assembly) vi
A
5.4. Gọi phương thc khi tạo ca lp sở
Trong trên một lớp mới tên LuongMoi được dẫn xuất tlớp cơ sở Luong, lớp
LuongMoi hai phương thức khởi tạo mt phương thức khởi tạo không tham số một
public class MyClass
{
//...
protected int myValue;
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 79
phương thức khi tạo ba tham số. Trong hai phương thức khởi tạo của lớp dẫn xuất này
gi phương thức khởi tạo của lớp sở. Cách gọi được thực hiện bằng việc đặt dấu
hai chấm ngay sau phần khai o danh sách tham số tham chiếu đến lớp sở thông
qua t khóa base:
Bởi các lớp không được kế thừa các phương thức khởi dựng của lớp sở, do đó lớp
dẫn xuất phi thực thi phương thức khởi dựng riêng của nó. chỉ thể sử dụng
phương thức khởi dựng của lớp cơ sở thông qua việc gọi tường minh.
Một điều lưu ý trong dụ trên là việc lớp LuongMoi thực thi mt phiên bản mới của
phương thức Nhap() và TinhLuong():
public new void Nhap()
public new double TinhLuong()
Từ khóa new được sử dụng đây để chỉ ra rằng người lập trình đang tạo ra một phiên
bản mới cho phương thức này bên trong lớp dẫn xuất. Nếu lớp sở phương thức
khởi dựng mặc định, t lớp dẫn xuất không cần bắt buộc phi gọi phương thức khởi
dựng của lớp smt cách tường minh. Thay vào đó phương thức khởi dựng mặc định
của lớp sở sẽ được gọi một cách ngầm định. Tuy nhiên, nếu lớp sở không
phương thức khởi dựng mặc định, t tất cả các lớp dẫn xuất của phải gọi phương thức
khởi dựng của lớp cơ sở một cách tường minh thông qua việc sử dụng từ khóa base.
5.5. Truy xuất các thành phn của lớp sở
Trong dụ trên phương thức Nhap() của lớp LuongMoi sẽ làm ẩn và thay thế
phương thức Nhap của lớp sở Luong. Khi chúng ta gọi phương thức Nhap của một đối
tượng của lớp LuongMoi thì phương thức LuongMoi.Nhap() sẽ được thực hiện, không
public LuongMoi(): base()
{
HeSoPhuCap = 0.4;
}
public LuongMoi(int lcb, double hsl, double hspc): base(lcb, hsl)
{
HeSoPhuCap = hspc;
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 80
phải phương thức Luong.Nhap() của lớp sLuong. Tuy nhiên, ta có thể gi phương
thức Nhap() của lớp cơ sở thông qua t khóa base:
base.Nhap(); // gọi phương thức sở
Từ khóa base chỉ đến lớp sở cho đi tượng hiện hành
5.6. Boxing Unboxing dữ liu
Boxing unboxing những xử cho phép kiểu dữ liệu giá trị (như int,
long,...) được đối xử như kiểu dữ liệu tham chiếu (các đối tượng). Một giá trị được
đưa vào bên trong của đối tượng, được gọi Boxing. Trường hợp ngược lại,
Unboxing sẽ chuyển tđối tượng ra mt giá trị. Xử này đã cho phép chúng ta gi
phương thức ToString( ) trên kiểu dữ liệu int trong ví dụ 3.4.
Boxing được thực hiện ngầm định
Boxing một sự chuyển đổi ngầm định của một kiểu dữ liệu giá trị sang kiu
dữ liệu tham chiếu là đối tượng. Boxing mt giá tr bng cách to ra mt thhiển của
đối tượng cần dùng sao chép g trị trên vào đi tượng mới tạo. Ta hình vẽ
sau minh họa quá trình Boxing mt số nguyên.
Boxing số nguyên.
Boxing được thực hiện ngầm định khi chúng ta đặt mt kiểu g trị o một tham
chiếu đang chờ đợi giá trị sẽ được đưa vào đối tượng mt cách tự động ngầm định.
dụ, nếu chúng ta gán một kiểu liệu bản như kiểu nguyên int
o mt biến
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 81
kiểu Object (điều này hoàn toàn hợp lệ kiểu int được dẫn xuất từ lớp Object) t
giá trị này sẽ được đưa vào biến Object, như minh ha sau:
Unboxing phải được thực hiện tường minh
Việc đưa một giá tr o một đi tượng được thực hiện mt cách ngầm định.
sự thực hin ngược li, unboxing, tức là đưa tmt đối tượng ra mt giá trị phải được
thực hin mt cách tường minh. Chúng ta phải thiết lập theo hai bước sau:
Phải chắc chắn rằng đối tượng đã boxing đúng kiểu giá trị đưa ra.
Sao chép giá tr từ thể hiện hay đối tượng vào biến kịểu giá trị.
Unboxing sau khi thực hiện Boxing.
Để thực hiện unboxing thành ng, thì đối tượng được unboxing phải được tham
chiếu đến mt đối tượng, đối tượng y đã được tạo ra bằng việc boxing mt giá
using System;
class Boxing
{
public static void Main()
{
int i = 123;
Console.WriteLine(“The object value = {0}”, i);
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 82
trị cùng vi kiểu của giá trị được đưa ra. Boxing Unboxing được minh họa trong ví
dụ sau
dụ : Boxing Unboxing.
dụ trên tạo một số nguyên i thực hiện boxing ngầm định khi i được gán cho
mt đối tượng o. Sau đó giá trị được unboxing mt cách tường minh gán đến mt
biến nguyên int mới, và cuốing giá tr được hiển thị.
Thông thường, chúng ta sẽ bao bọc các hoạt động unboxing trong khối try. Nếu mt đối
tượng được Unboxing null hay tham chiếu đến mt đối tượng kiểu dữ liệu khác,
mt InvalidCastException sẽ được phát sinh.
5.7. Các lớp lng nhau
Các lớp chứa những thành viên, những thành viên này th mt lớp khác
kiểu do người ng định nghĩa (user-defined type). Do vậy, mt lớp Button th
một thành viên của kiểu Location, kiểu Location này chứa thành viên của kiểu
dữ liệu Point. Cuối cùng, Point có thể chứa chứa thành viên của kiểu int.
Cho đến lúc này, các lớp được tạo ra chỉ để dùng cho các lớp bên ngoài, chức
năng của các lớp đó như là lớp trợ giúp (helper class). Chúng ta thđịnh nghĩa mt
lớp trợ giúp n trong các lớp ngoài (outer class). Các lớp được định nghĩa bên
trong gọi các lớp lng (nested class), và lớp chứa được gọi đơn gin là lớp ngoài.
using System;
public class UnboxingTest
{
public static void Main()
{
int i = 123;
// Boxing
object o = i;
// Unboxing phải được tường minh
int k = (int) o;
Console.WriteLine(“k: {0}”, k);
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 83
Những lớp lng bên trong lợi kh năng truy cập đến tất cả các thành viên
của lớp ngoài. Một phương thức của lớp lng thể truy cập đến biến thành viên
private của lớp ngoài. Hơn na, lớp lng bên trong th ẩn đối với tất cả các lp
khác, lớp lồng có thể là private cho lớp ngoài.
Cuối cùng, mt lớp làm lng bên trong là public được truy cập n trong phạm vi
của lớp ngoài. Nếu một lớp Outer là lớp ngoài, lớp Nested là lớp public lng bên
trong lớp Outer, chúng ta thể tham chiếu đến lp Tested như Outer.Nested, khi
đó lớp bên ngoài hành động ít nhiều ging như một namespace hay mt phạm vi.
Ghi chú: Đi với người lập trình Java, lớp lng nhau trong C# t ging như những
lớp ni static (static inner) trong Java. Không sự tương ứng trong C# vi
những lớp ni nonstatic (nonstatic inner) trong Java.
dụ 3.4 sau sẽ thêm mt lớp lng vào lớp Fraction tên là FractionArtist. Chức năng
của lớp FractionArtis là vẽ mt phân số ra màn hình. Trong dụ này, việc vẽ sẽ được
thay thế bng sử dụng hàm WriteLine xuất ra màn hình console.
dụ: Sử dụng lớp lồng nhau.
using System;
using System.Text;
public class Fraction
{
public Fraction( int numerator, int denominator)
{
this.numerator = numerator;
this.denominator = denominator;
}
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.AppendFormat(“{0}/{1}”,numerator, denominator);
return s.ToString();
}
internal class FractionArtist
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 84
Lớp Fraction trên i chung không thay đổi ngoại trừ vic thêm mt lớp
lng bên trong lược đi mt số phương thức không thích hợp trong dụ này. Lớp
lng bên trong FractionArtist chỉ cung cấp một phương thức thành viên duy nhất,
phương thức Draw(). Điều thú vị trong phương thức Draw() truy cập dữ liệu thành
viên private f.numerator và f.denominator.
Hai
viến
thành
viên
private y
sẽ
không
cho
phép
truy
cập
nếu FractionArtist không phải là lớp lng bên trong
của lớp Fraction.
Lưu ý trong hàm Main() khi khai báo một thể hiện của lớp lng bên trong, chúng
ta phải xác nhận tên của lớp bên ngoài, tức là lớp Fraction:
Fraction.FractionArtist fa = new Fraction.FractionArtist();
Thậm c khi lớp FractionArtist public, t phạm vị của lớp này vẫn nằm bên trong
của lớp Fraction.
public void Draw( Fraction f)
{
Console.WriteLine(“Drawing the numerator {0}”, f.numerator);
Console.WriteLine(“Drawing the denominator {0}”, f.denominator);
}
}
// biến thành viên private
private int numerator;
private int denominator;
}
public class Tester
{
static void Main()
{
Fraction f1 = new Fraction( 3, 4);
Console.WriteLine(“f1: {0}”, f1.ToString());
Fraction.FractionArtist fa = new Fraction.FractionArtist();
fa.Draw( f1 );
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 85
Bài 6: Kế tha đa hình(2)
6.1. Gii thiu chung về đa nh
hai cách thức để thực hiện việc kế thừa. Một sử dụng lại nguồn, khi
chúng ta tạo ra lớp HinhVuong, chúng ta thsử dụng li mt vài c thành phần
trong lớp cơ sở như HinhChuNhat.
Tuy nhiên, cách sử dụng thứ hai chứng tỏ được sức mạnh to lớn của vic kế thừa
đó là tính đa hình (polymorphism). Theo tiếng Anh từ này được kết hợp từ poly
nhiều morph nghĩa form (hình thức). Do vậy, đa hình được hiểu như là khả
năng sử dụng nhiều nh thức của mt kiểu không cần phi quan tâm đến từng chi
tiết.
Khi mt tổng đài đin thoại gởi cho máy điện thoại của chúng ta mt tín hiệu
cuộc gọi. Tổng đài không quan m đến điện thoại của ta là loi o. thể ta đang
dùng mt điện thoại dùng motor để rung chuông, hay một điện thoại điện t
phát ra tiếng nhạc số. Hoàn toàn các thông tin về điện thoại của ta không ý nghĩa
với tổng đài, tổng đài chỉ biết một kiểu bản điện thoi thôi diện thoại
này sẽ biết cách báo chuông. Còn việc o chuông như thế nào t tổng đài không quan
tâm. m lại, tng đài chỉ cần bảo điện thoại hãy làm điều gì đó để reng. n phần còn
lại tức cách thức reng tùy thuộc o từng loại đin thoại. Đây chính tính đa
hình.
Tính đa nh chính cách thực hiện cùng một cách thức với nhiều loại đối tượng
khác nhau.
6.2. Phương thc đa hình
Để xây dựng mt phương thức hỗ tr tính đa hình, ta đặt từ khóa
virtual trong phương thức của lớp sở. dụ, để chỉ định rằng phương thức
Hien Thi () của lớp DongVat trong dụ dưới đây đa nh, đơn gin là ta thêm t
khóa virtual vào khai báo trong phương thức như sau:
public virtual void HienThi()
{
Console.WriteLine("con {0} co {1} chan ,song o moi truong {2}", ten,
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 86
Lúc này tcác lớp dẫn xuất được tự do thực thi các cách xử riêng của
mình trong phiên bản mới của phương thức HienThi(). Để làm được điều này chỉ
cần thêm từ khóa override để chồng lên phương thức ảo HienThi () của lớp cơ sở.
Sau đó thêm các đoạn mã nguồn mi vào phương thức viết chng này.
Trong ví dụ minh họa sau, lớp ConMeo dẫn xụất từ lớp DongVat và thực thi mt phiên
bản riêng của phương thức HienThi():
Từ khóa override bảo với trình biên dịch rằng lớp này thực hiện việc phủ quyết lại
phương thức HienThi() của lớp sở. Tương tự như vy ta th thực hin việc
phủ quyết phương thức này trong mt lớp dẫn xuất khác được dẫn xuất tlớp
Dong Va t
dụ: Xây dựng phương thức ảo trong lớp sở, ghi đè phương thức ảo trong
lớp kế thừa
chan, moitruong);
}
public override void HienThi()
{
base.HienThi();
Console.WriteLine("con vat ma toi yeu thich la con {0} co {1}
chan,thich an {2}", ten, chan, moitruong);
}
using System;
using System.Collections.Generic;
using System.Text;
namespace vidu
{
class Program
{
static void Main(string[] args)
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 87
DongVat dongVat1 = new DongVat("ga", 2, "can");
ConMeo conmeo1 = new ConMeo("meo", 4, "can");
Console.WriteLine("ket qua cua lop dong vat");
dongVat1.HienThi();
Console.WriteLine("ket qua cua lop con meo ");
conmeo1.HienThi();
Console.ReadLine();
}
}
public class DongVat
{
protected string ten;
protected int chan;
protected string moitruong;
public DongVat(string ten, int chan, string moitruong)
{
this.ten = ten;
this.chan = chan;
this.moitruong = moitruong;
}
//phuong thuc duoc khai bao la ao
public virtual void HienThi()
{
Console.WriteLine("con {0} co {1} chan ,song o moi truong {2}", ten,
chan, moitruong);
}
}
public class ConMeo : DongVat
{
public ConMeo(string ten, int chan, string moitruong)
: base(ten, chan, moitruong)
{
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 88
Đoạn chương trình trên chúng ta thấy tính đa hình chưa được thể hiện trong chương
tnh Main. Tính đa hình sẽ được thhin rõ trong trường hợp như sau:
Khi thực hiện đoạn chương trình trên trình biên dịch biết rằng mng hai đối
tượng DongVat phải thực hiện thực thi phương thức HienThi cho các đối tượng này.
Nếu chúng ta không đánh dấu phương thức HienThi trong lớp DongVat là virtual t
phương thức HienThi của lớp DongVat s được gọi 2 ln. Tuy nhiên do chúng ta đã
đánh dấu phương thức HienThi lớp sở là phương thức ảo và thực thi phủ quyết
lớp dẫn xuất
Khi ta gọi phương thức HienThi trong mảng, tnh biên dịch sẽ ra được chính
c kiểu dữ liệu nào được thực thi trong mảng khi đó hai kiểu sẽ được thực thi
mt DongVat, mt ConMeo. trình biên dịch sẽ gọi chính c phương thức của
từng đi tượng.
Ta thấy rằng mảng dv được khai báo là đối tượng lớp DongVat. Nhưng khi nhận gtrị
t dv[0] được gán cho một đối tượng lớp DongVat, dv[1] được gán cho mt đối tượng
lớp ConMeo. Như vậy phương thức dv[0].HienThi() t sẽ thực thi phương thức
HienThi của lớp DongVat, còng dv[1].HienThi() thì sẽ thực thi phương thức HienThi
của lớp ConMeo. Ta thấy dv[0] dv[1] hai loại khác nhau nhưng li cùng một
//phu quyet phuong thuc HienThi cua lop co so
public override void HienThi()
{
base.HienThi();
Console.WriteLine("con vat ma toi yeu thich la con {0} co {1}
chan,thich an {2}", ten, chan, moitruong);
}
}
DongVat[] dv = new DongVat[2];
dv[0] = new DongVat("Cho", 4, "Vat nuoi can");
dv[1] = new ConMeo("Meo muop", 4, "Vat nuoi can");
dv[0].HienThi();
dv[1].HienThi();
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 89
cách thức thực hiện phương thức HienThi() là như nhau đây chính là thể hiện tính đa
hình.
Lưu ý: Nếu phương thức của lớp sở là phương thức ảo, ta thphủ quyết lớp ảo
lớp dẫn xuất băng t khóa override, hoặc không phủ quyết phương thức ảo, khi đó
phương thức ảo ở lớp cơ sở trthành phương thức ảo ở lớp dẫn xuất.
6.3. Từ khoá new override
Từ khóa new override
Trong nn ngữ C#, người lập trình thể quyết định phủ quyết mt phương
thức ảo bằng cách khai báo tường minh tkhóa override. Điều này giúp cho ta đưa ra
mt phiên bản mới của chương trình sự thay đổi của lớp sở s không làm nh
hưởng đến chương trình viết trong các lớp dẫn xuất. Việc yêu cầu sử dụng t khóa
override sẽ giúp ta ngăn ngừa vấn đề này.
Bây giờ ta thử bàn về vấn đề này, gis lớp sở DongVat của dụ trước được
viết bởi mt công ty A. Cũng gisử rằng lớp ConMeo đươc viết tnhng người lập
tnh của công ty B họ dùng lớp cơ sở DongVat mua được của ng ty A làm lớp
sở cho lớp trên. Người lập tnh trong công ty B không hoặc rất ít sự kim soát
về những thay đổi trong tương lai với lớp DongVat do công ty A phát trin.
Khi nhóm lập trình của công ty B quyết định thêm một phương thức Keu( ) o lớp
ConMeo:
Việc thêm vào vẫn bình thường cho đến khi ng ty A, tác gicủa lớp sở DongVat,
đưa ra phiên bản th hai của lớp DongVat. trong phiên bn mới này những người
lập trình của công ty A đã thêm mt phương thức Keu( ) vào lớp cơ sở DongVat:
public virtual void Keu(string tiengkeu)
{
Console.WriteLine("Con {0} keu {1}", ten, tiengkeu);
}
public class Window
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 90
Trong các ngôn ngữ lập trình hướng đối tượng khác như C++, phương thức ảo mới
Keu() trong lớp Dong Vat bây giờ sẽ hành động giống như mt phương thức
sở cho phương thức ảo trong lớp ConMeo. Trình biên dch thể gọi phương thức
Keu( ) trong lớp ConMeo khi chúng ta ý định gọi phương thức Keu( ) trong
Dong Vat. Trong nn ngữ Java, nếu phương thức Keu( ) trong DongVat kiểu
trả về khác kiểu trả về của phương thức Keu( ) trong lớp ConMeo t sẽ được báo lỗi
phương thức phquyết không hợp lệ.
Ngôn ngữ C# ngăn ngừa sự ln ln này, trong C# mt phương thức ảo thì được xem
như gốc rễ của sự phân phi ảo. Do vậy, mt khi C# tìm thấy mt phương thức khai
o ảo thì sẽ không thực hiện bất cứ việc tìm kiếm nào trên cây phân cấp kế
thừa. Nếu mt phương thức ảo Keu( ) được trình bày trong lớp DongVat, t khi thực
hiện hành vi của lớp ConMeo không thay đổi.
Tuy nhiên khi biên dch li, t trình biên dịch sẽ đưa ra mt cảnh o giống như sau:
Để loại bỏ cảnh báo này, người lập trình phải chỉ ý định của anh ta. Anh ta thể
đánh dấu phương thức ConMeo.Keu( ) với từ khóa new, không phải phủ
quyết của bất cứ phương thức ảo nào trong lớp DongVat:
//……..
public virtual void Keu(string tiengkeu)
{//……………. }
}
…. 'LTHDTC.ConMeo.Keu(string)' hides inherited member
'LTHDTC.DongVat.Keu(string)'. To make the current member override that
implementation, add the override keyword. Otherwise add the new keyword.
C:\Tai lieu giang day\Lap trinh huong doi tuong\Vi
du\LTHDTC\LTHDTC\DongVat.cs 53 25 LTHDTC
public class ConMeo : DongVat
{
public new virtual Keu( ) {….}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 91
Việc thực hiện khai báo trên sẽ loại b được cảnh báo. Mặc khác nếu người lập trình
muốn phủ quyết mt phương thức trong DongVat, t anh ta cần thiết phải ng từ
khóa override để khai báo một cách tường minh:
Public class ConMeo:DongVat
{
//……
Public override Keu(){.............. }
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 92
Bài 7: Kế tha đa hình(3)
7.1 Lớp trừu tưng
7.1.1. Xây dựng lớp sở trừu tượng
Ta mt lớp Hinh các lớp HinhChuNhat, HinhTron, HinhVuong kế thừa tlớp
Hinh, lớp hình các phương thức tính chu vi, tính diện tích, Vẽ. Nhưng với mỗi mt
loại nh t cách tính diện tích, chu vi, v là khác nhau. Nghĩa với các phương
thức trên thì mi mt lớp kế thừa từ lớp Hinh đòi hỏi phi thực thi mt phương thức
cho riêng mình. Tuy nhiên nếu như chúng ta khai báo các phương thức trên trong lớp
Hinh là phương thức ảo t điều này không thực sự đòi hi các lớp kế thừa phải ghi đè
các phương thức trên mt cách bắt buộc. Để yêu cầu các lớp dẫn xuất bắt buộc phải
thực thi mt phương thức của lớp cơ sở thì phương thức đó chúng ta phải khai báo
phương thức trừu tượng.
Một phương thức trừu tượng không sự thực thi. Phương thức này chỉ đơn giản
to ra mt tên phương thức ký hiệu của phương thức, phương thức này sđược thực
thi c lớp dẫn xuất. Một lớp chứa ít nhất mt phương thức trừu tượng t lớp đó
phải lớp trừu tượng.
Những lớp trừu tượng được thiết lập như scho những lớp dẫn xuất, nhưng
việc tạo các thể hiện hay các đối tượng cho các lớp trừu tượng được xem không
hợp lệ. Một khi chúng ta khai báo mt phương thức trừu tượng, t chúng ta phải
ngăn cấm bất cứ việc tạo thể hin cho lớp này.
Do vy, nếu chúng ta thiết kế phương thức Chu Vi () như trừu tượng trong
lớp Hinh, chúng ta thể dẫn xuất tlớp này, nhưng ta không th tạo bất cứ đối tượng
cho lớp y. Khi đó mi lớp dẫn xuất phải thực thi phương thức ChuVi(). Nếu lớp
dẫn xuất không thực thi phương thức trừu tượng của lớp sở t lớp dẫn xuất đó
cũng lớp trừu tượng, và ta cũng không thể tạo các thể hiện của lớp này được.
Phương thức trừu tượng được thiết lập bằng cách thêm tkhóa abstract vào đầu của
phần định nghĩa phương thức, cú pháp thực hiện như sau:
abstract public double ChuVi();
Do phương thức không cần phần thực thi, nên không dấu ({}) chỉ dấu chấm
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 93
phẩy (;) sau phương thức. Như thế với phương thức ChuVi () được thiết kế trừu
tượng t chỉ cần câu lệnh trên là đủ.
Nếu mt hay nhiều phương thức được khai báo là trừu tượng, thì phần đnh nghĩa
lớp phải được khai báo là abstract, vi lớp Hinh ta có thể khai báo là lớp trừu tượng
như sau:
dụ : Phương thức trừu ợng
abstract class Hinh
{
//……..
}
class Diem
{
private int x;
private int y;
public Diem(int x, int y)
{
this.x = x;
this.y = y;
}
public int X
{
set
{
x = value;
}
get
{
return x;
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 94
public int Y
{
get
{
return y;
}
set
{
y = value;
}
}
public void Draw()
{
System.Console.WriteLine("Toa do x {0}, y {1}", x, y);
}
public double KhoangCach(Diem d)
{
return Math.Sqrt(Math.Pow((x - d.x), 2) + Math.Pow((y - d.y), 2));
}
public double KhoangCach(int x, int y)
{
return Math.Sqrt(Math.Pow((x - this.x), 2) + Math.Pow((y - this.y), 2));
}
}
// Vì lớp có phương thức trừu tượng, nên lớp cũng phải khai báo là lớp trừu tượng
abstract class Hinh
{
// Phương thức trừu tượng
abstract public void Draw();
abstract public double DienTich();
abstract public double ChuVi();
public void thu()
{
System.Console.WriteLine("chao tat ca nhung ai dang su dung lop");
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 95
7.1.2. Kế thừa từ lớp trừu tượng
Khi mt lớp được dẫn xuất từ mt lớp trừu tượng thì lớp đó được kế thừa tất cả
các thành phần không phi private của lớp sở. Trong đó cả các thành phần là trừu
tượng không trừu tượng, khi đó lớp dẫn xuất bắt buộc phải ghi đè tt cả các phương
thức trừu tượng bng tkhóa override. Nếu lớp dẫn xuất không ghi đè hết các phương
thức trừu tượng thì lớp dẫn xuất sẽ là lớp trừu tượng.
dụ : Xây dựng lớp kế thừa từ lớp trừu tượng.
}
}
}
class HinhTron : Hinh
{
private Diem tam;
private double bankinh;
public HinhTron(Diem tam, double bankinh)
{
this.tam = tam;
this.bankinh = bankinh;
}
public HinhTron(int x, int y, double bankinh)
{
tam = new Diem(x, y);
this.bankinh = bankinh;
}
// Thực thi phương thức trừu tượng trong lớp cơ sở
public override void Draw()
{
System.Console.Write("Duong tron co:\n Toa do tam");
tam.Draw();
System.Console.WriteLine("Ban kinh: {0}", bankinh);
}
// Thực thi phương thức trừu tượng trong lớp sở
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 96
public override double ChuVi()
{
return 2 * Math.PI * bankinh;
}
// Thực thi phương thức trừu tượng trong lớp sở
public override double DienTich()
{
return Math.PI * bankinh * bankinh;
}
}
class HinhChuNhat : Hinh
{
protected double a, b;
public HinhChuNhat(double a, double b)
{
if ((a <= 0) | (b <= 0))
{
throw new ArithmeticException("Canh cua hinh chu nhat phai lon hon 0");
}
else
{
this.a = a;
this.b = b;
}
}
public override void Draw()
{
System.Console.WriteLine("Hinh chu nhat co hai canh \na= {0}\nb= {1}", a, b);
}
public override double ChuVi()
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 97
Khi đó ta thể sử dụng các lớp trên để khai báo như sau :
{
return (a + b) * 2;
}
public override double DienTich()
{
return (a * b);
}
}
class HinhVuong : HinhChuNhat
{
public HinhVuong(double a)
: base(a, a)
{ }
public override void Draw()
{
System.Console.WriteLine("Hinh vuong co canh la {0}", a);
base.Draw();
}
}
public static void Main()
{
Hinh h ;
HinhTron ht = new HinhTron(5, 7, 16);
HinhChuNhat hcn = new HinhChuNhat(5, 8);
HinhVuong hv = new HinhVuong(8);
h = ht;
System.Console.WriteLine(h.ChuVi());
h = hcn;
System.Console.WriteLine(h.ChuVi());
h = hv;
System.Console.WriteLine(h.ChuVi());
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 98
Khi đó đoạn chương trình trên hoàn toàn chạy được không gặp bất cứ một li nào cả.
Nhưng nếu ta thay câu lnh Hinh h bằng câu lệnh Hinh h = new Hinh() ; thì trình biên
dịch sẽ báo li như sau :
Cannot create an instance of the abstract class or interface Hinh ……….
Quay trở lại tính đa hình mà chúng ta đã bàn trước đây, chúng ta muốn các loi đối tượng
khác nhau khi thực hiện một hành động cùng mt cách thức với nhau. Như trong chương
tnh Main trên t biến đối tượng Hinh h được gán cho đối tượng HinhTron,
HinhChuNhat, HinhVuong, như vậy khi gọi các phương thức thì trình biên dch sẽ kiểm
tra xem đối tượng h là đối tượng nào để gọi các phương thức tương ứng.
Sau đây mt dụ tính đa hình được thể hiện rất rõ.
Như vậy thay phải xây dựng các phương thức khác nhau cho các kiểu đối tượng
hình khác nhau, ta thxây dựng mt phương thức đối số truyền vào là lớp cha của
các lớp đó. Như vậy khi gọi tta thtruyền vào các biến đối tượng kế thừa tlp
cha đó. Như vậy chúng ta thấy ràng rằng đối tượng hình thì khi muốn thực
public static void Main()
{
Hinh h;
HinhTron ht = new HinhTron(5, 7, 16);
HinhChuNhat hcn = new HinhChuNhat(5, 8);
HinhVuong hv = new HinhVuong(8);
ThaoTacHinh(ht);
ThaoTacHinh(hv);
ThaoTacHinh(new HinhChuNhat(5, 8));
ThaoTacHinh(new HinhTron(5, 6, 7));
}
public static void ThaoTacHinh(Hinh h)
{
System.Console.WriteLine(h.ToString());
h.Draw();
System.Console.WriteLine("Chu vi: {0}", h.ChuVi());
System.Console.WriteLine("Dien tich: " + h.DienTich());
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 99
hiện các thao tác ta đều mt cách thức chung gọi phương thức ThaoTacHinh
truyền vào đối tượng Hình cần thực hin. Đó chính là tính đa hình.
7.1.3. Hạn chế của lớp trừu ợng
Mặc chúng ta đã thiết kế phương thức ChuVi() như một phương thức trừu
tượng để hỗ trợ cho tất cả các lớp dẫn xuất được thực thi riêng, nhưng điều này
mt số hạn chế. Nếu chúng ta dẫn xuất mt lớp t lớp HinhChuNhat như lớp
HinhVuong, t lớp này không được hỗ trđể thực thi phương thức ChuVi( ) cho riêng
nó.
Ghi chú: Khác với nn ngữ C++, trong C# phương thức Hinh.ChuVi( ) không thể
cung cấp mt sự thực thi, do đó chúng ta sẽ không thể lấy được li ích của phương
thức ChuVi() bình thường dùng để chia xbởi các lớp dẫn xuất.
Cuối cùng những lớp trừu tượng không có sự thực thi căn bản; chúng thể hiện ý tưởng
về mt sự trừu tượng, điều này thiết lập mt sự giao ước cho tất cả các lớp dẫn xuất.
Nói cách khác các lớp trừu tượng tả một phương thức chung của tất cả các lớp
được thực thi mt cách trừu tượng.
Ý tưởng của lp trừu tượng Hinh thể hiện những thuộc tính chung cùng với nhng
hành vi của tất cả các Hình, thm chí ngay cả khi ta không ý định tạo thể hiện
của chính lớp trừu tượng Hinh.
Ý nghĩa của mt lớp trừu tượng được bao hàm trong chính ttrừu tượng”. Lớp y
dùng để thực thi mt Hinh” trừu tượng, sẽ được biểu lộ trong các thể hiện
c định của Hinh, như là HinhTron, HinhVuong, HinhChuNhat…
Các lớp trừu tượng không thể thực thi được, chỉ nhng lớp xác thực tức những
lớp dẫn xuất từ lớp trừu tượng này mi thể thực thi hay tạo thể hiện. Một sự thay
đổi việc sử dụng trừu tượng là định nghĩa mt giao din (interface), phần này sẽ được
tnh bày trong Chương tiếp theo
Một số chú ý:
Không th tạo đối tượng của lớp trừu tượng
Một lớp chứa một phương thức trừu tượng thì lớp đó phải lớp trừu
tượng
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 100
Lớp dẫn xuất không hin thực tất cả các phương thức trừu tượng của lớp
cơ sở t lớp đó cũng phải là lớp trừu tượng
Để khai o phương thức trừu tượng ta sử dụng từ khoá abstract
Phương thức trừu tượng không phần thân
Phương thức trừu tượng không thể private
Phương thức trừu tượng mặc định cũng phương thức ảo
Phương thức tĩnh không thể phương thức trừu tượng
Lớp dẫn xuất kề thừa phương thức trừu tượng t phải ghi đè nó.
Phương thức trừu tượng thể ghi đè phương thức của lớp sở được
khai o là phương thức ảo
Phương thức trừu tượng có thể ghi đè phương thức của lớp cơ sở đã đã
được ghi đè
7.2. Lp lập (sealed class)
Ngược với các lớp trừu tượng là các lớp lập. Một lớp trừu tượng được thiết kế
cho các lớp dẫn xuất cung cấp các khuôn mẫu cho các lớp con theo sau. Trong khi
mt lớp lập t không cho phép các lớp dẫn xuất từ nó. Để khai báo mt lớp
lập ta dùng từ khóa sealed đặt trước khai báo của lớp không cho phép dẫn xuất.
Hầu hết các lớp thường được đánh dấu sealed nhằm ngăn chặn các tai nạn do sự kế
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 101
thừa gây ra.
Nếu khai báo của lớp Hinh trong dụ 3.2 được thay đổi từ khóa abstract bằng t
khóa sealed (cũng thể loại bỏ tkhóa trong các khai báo của phương thức ChuVi(),
DienTich(),…). Chương trình sẽ bị li khi biên dịch. Nếu chúng ta cố thử biên dch
chương trình thì sẽ nhn được li từ trình biên dch:
‘HinhTron’ cannot inherit from sealed class Hinh
Đây chỉ mt li trong số những li như ta không thể tạo một phương thức thành
viên
protected trong mt lớp khai o sealed.
7.3. Lp Object
Tất cả các lớp của ngôn ngữ C# của bất cứ kiểu dliệu nào t cũng được dẫn xuất t
lớp System.Object. Thú vị là bao gồm cả các kiểu dữ liu giá trị.
Một lớp sở cha trực tiếp của một lớp dẫn xuất. Lớp dẫn xuất này ng thlàm
sở cho các lớp dẫn xuất xa hơn nữa, việc dẫn xuất này sẽ tạo ra một cây thừa kế
hay mt kiến trúc phân cấp. Lớp gốc là lớp nằm trên cùng cây pn cấp thừa kế, n
các lớp dẫn xuất t nằm bên dưới. Trong ngôn ngữ C#, lớp gốc lớp Object, lớp
này nằm trênng trong cây phân cấp các lớp.
Lớp Object cung cấp mt s các phương thức dùng cho các lớp dẫn xuất thể thực
hiện việc ph quyết. Những phương thức này bao gồm Equals() kim tra xem hai đối
tượng ging nhau hay không. Phương thức GetType() trả về kiểu của đối tượng.
phương thức ToString() trả về mt chuỗi thể hiện lớp hiện hành. Sau đây bảng m
tt các phương thức của lớp Object.
Phương thức
Chc năng
Equal( )
So sánh bằng nhau giữa hai đối tượng
GetHashCode( )
Cho phép những đối tượng cung cấp riêng
những hàm băm cho sử dụng tập hợp.
GetType( )
Cung cấp kiểu của đối tượng
ToString( )
Cung cấp chuỗi thể hiện của đối tượng
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 102
Finalize( )
Dọn dẹp các tài nguyên
MemberwiseClone( )
Tạo mt bản sao từ đối tượng.
Bảng 3.1: Tóm tắt các phương thức của lớp Object.
dụ: Sau minh họa việc sử dụng phương thức ToString( ) thừa kế từ lớp Object.
Trong tài liệu của lớp Object phương thức ToString() được khai báo như sau:
public virtual string ToString();
Đây phương thức ảo public, phương thức này trvề mt chuỗi và không nhận tham
số. Tất cả kiểu dữ liệu được xây dựng sẵn, như kiểu int, dẫn xuất từ lớp Object nên
cũng có thể thực thi các phương thức của lớp Object.
Lớp H inh Chu N hat
trong
dụ
trên
thực
hiện
việc
phủ
quyết phương
thc
class HinhChuNhat
{
protected double a, b;
public HinhChuNhat(double a, double b)
{
if ((a <= 0) | (b <= 0))
{
throw new ArithmeticException("Canh cua hinh chu nhat phai lon hon 0");
}
else
{
this.a = a;
this.b = b;
}
}
public override string ToString()
{
return "Hinh chu nhat";
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 103
ToString(), do đó phương thức này sẽ tr về g tr nghĩa. Nếu chúng ta không
phủ quyết phương thức ToString() trong lớp HinhChuNhat, phương thức của lớp
sở sẽ được thực thi
Như chúng ta thy, hành vi mặc định đã trả về một chuỗi chính tên của lớp đang thể
hiện. Các lớp không cần phải khai o tường minh việc dẫn xuất từ lớp Object,
việc kế thừa sẽ được đưa vào một cách ngầm định. Như lớp HinhChuNhat trên ta
không khai báo bất cứ dẫn xuất của lớp nào nhưng C# sẽ tự đng đưa lớp Object
thành lớp dẫn xuất. Do đó ta mới thể phủ quyết phương thức ToString() của lớp
Object.
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 104
Bài 8: Giao din
8.1. Giao din
8.1.1. Xây dựng giao diện
Giao diện ràng buộc, giao ước đảm bảo cho các lớp hay các cấu trúc sẽ
thực hin mt điều đó. Khi mt lớp thực thi mt giao din, thì lớp này báo cho c
thành phn client biết rằng lớp này có hỗ trợ các phương thức, thuộc tính, sự kin
các chỉ mục khai báo trong giao din.
Một giao din đưa ra mt sự thay thế cho các lớp trừu tượng để tạo ra các sự
ràng buộc giữa nhng lp các thành phần client của nó. Nhng ng buộc này
được khai o bằng cách sử dụng từ khóa interface, từ khóa này khai o mt kiu
dữ liu tham chiếu để đóng gói các ng buộc.
Một giao diện:
Gần ging như lớp trừu tượng, chỉ phần khai o không phần trin
khai
Chỉ chứa phương thức, thuộc tính, chỉ mục sự kiện
Thành phần của Interface mặc định public abstract (virtual)
Không chứa thành phần nh
thể trin khai từ mt giao diện khác
Một lớp thể triển khai từ nhiều giao diện
Một lớp trừu tượng được dùng làm lớp sở cho mt họ các lớp dẫn xuất từ nó.
Trong khi giao din là sự trộn lẫn với các cây kế thừa khác.
Khi mt lớp thực thi mt giao din, lớp y phải thực thi tất cả các phương
thức của giao diện. Đây là mt bắt buộc mà các lớp phải thực hiện.
pháp để định nghĩa mt giao diện như sau:
[thuộc tính] [Bổ sung truy cập] interface <tên giao diện> [: danh ch sở]
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 105
Phần thuộc tính chúng ta sẽ đề cập sau. Thành phần bổ sung truy cập bao
gồm: public, private, protected, internal, protected internal, ý nghĩa tương t
như các bsung truy cập của lớp.
Theo sau từ khóa interface tên của giao diện. Thông thường tên của giao
diện được bắt đầu vi từ I hoa (điều này không bắt buộc nhưng việc đặt tên như vậy
rất rõ ràng và dễ hiểu, tránh nhầm lẫn với c thành phần khác).
Danh sách sở danh sách các giao din giao din này mở rộng, Phần
thân của giao diện chính phần thực thi giao diện sẽ được trình bày bên dưới.
Gi sử chúng ta muốn tạo một giao din nhm mô tả những phương thức và
thuộc tính của mt lớp cần thiết để lưu trữ truy cập từ một sở dữ liệu hay c
thành phần lưu trữ dữ liệu khác như một tập tin. Chúng ta quyết định gọi giao din
này là IStorage.
Trong giao diện này chúng ta xác nhận hai phương thức: Read() Write(),
khai o này sẽ được xuất hin trong phần thân của giao diện như sau:
Mục đích của mt giao diện để định nghĩa những khả năng mà chúng ta
muốn có trong mt lớp.
8.1.2. Khai báo thuộc tính của giao diện
Thuộc tính được khai báo trong giao din không phần thực thi cho get()
set() mà chỉ đơn giản là khai báo có hành vi là get() và set():
<phn thân giao diện>
}
interface IHinh
{
Double ChuVi();
Double DienTich();
void InThongTin();
}
Kieudulieu TenThuocTinh
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 106
dụ: Trong giao diện Ihinh ta khai báo thuộc tính TenHinh chỉ trả về giá trị như sau:
8.1.3. Khai báo phương thức của giao diện
Khi định nghĩa c phương thức của giao din không phần b sung truy cập (ví dụ
như: public, protected, internal, private). Việc cung cấp các bổ sung truy cập sẽ to ra
mt li. Những phương thức của giao din được ngầm định public giao diện
những ràng buộc được sử dụng bởi những lớp khác. Chúng ta không thể tạo một thể
hiện của giao din, thay vào đó chúng ta sẽ tạo thể hin của lớp thực thi giao din.
Khai báo phương thức theo cú pháp sau:
dụ 4.1: Xây dựng giao diện Ihinh
{
[ get;]
[ set;]
}
string TenHinh
{
get;
}
kieuDuLieu[void] TenPhuongThuc(khai o các tham số);
interface IHinh
{
// Khai báo thuộc tính của giao din
string TenHinh
{
get;
}
// Khai báo các phương thức của giao diện
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 107
8.2. Thc thi giao din
8.2.1. Thực thi giao diện
Khi xây dựng mt lớp ta có thể kế thừa từ một lớp cơ sở và thực thi mt hoặc
nhiều giao diện. Để thực thi giao diện ta đặt giao diện sau dấu : sau phần khai báo tên
lớp, nếu lớp kế thừa mt lớp cơ sở và thực thi nhiu giao diện t lớp cơ sở dứng trước
các giao din, lớp cơ sở và giao diện ngăn cách nhau bi dấu phy (,). Khi một lớp thực
thi mt giao din thì lớp đó phải thực thi tất cả các thuộc tính và phương thức khai báo
trong giao diện. Nếu không thực thi hết thì chương trình dịch sẽ báo li
dụ 4.1 (tiếp) thực thi giao diện
double ChuVi();
double DienTich();
void InThongTin();
}
using System;
namespace LTHDTC
{
class Diem
{
protected int x,y;
public Diem (int x, int y)
{
this.x=x;
this.y=y;
}
public int X
{
get
{
return x;
}
set
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 108
{
x = value;
}
}
public int Y
{
get
{
return y;
}
set
{
y = value;
}
}
}
interface IHinh
{
string TenHinh
{
get;
}
double ChuVi();
double DienTich();
void InThongTin();
}
class HinhTron:IHinh
{
Diem tam;
double bankinh;
public HinhTron(int x, int y, double bankinh)
{
tam = new Diem(x, y);
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 109
Truy cập phương thức giao diện
Chúng ta có thể truy cập những thành viên của giao diện IHinh như thể các tnh
viên của lớp HinhTron
hay ta thtạo thể hiện của giao diện bng cách gán đối tượng HinhTron cho mt
kiểu dữ liệu giao din, và sau đó sử dụng giao din này để truy cập các phương thức:
Ghi chú: Chúng ta không thể to th hiện của giao din một cách trực tiếp.Do đó
this.bankinh = bankinh;
}
public string TenHinh
{
get { return "Hinh Tron"; }
}
public double ChuVi()
{
return Math.PI * bankinh * 2;
}
public double DienTich()
{
return Math.PI * bankinh * bankinh;
}
public void InThongTin()
{
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh,
DienTich(), ChuVi());
}
}
}
HinhTron h = new HinhTron(4, 7, 4);
h. InThongTin();
HinhTron h = new HinhTron(4, 7, 4);
IHinh hinh = (IHinh)h;
Hinh. InThongTin();
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 110
chúng ta không thể thực hin như sau:
Ihinh hinh=new IHinh();
Tuy nhiên chúng ta thể tạo thể hiện của lớp thực thi giao din như sau:
Sau đó chúng ta có thể to thể hin của giao diện bằng cách gán đối tượng thực thi
đến kiểu dữ liệu giao din, trong trường hợp này là IHinh
IHinh hinh= (IHinh) h;
Chúng ta thể kết hợp nhng bước trên như sau:
IHinh hinh= (IHinh) new HinhTron(4, 7, 4);
Nói chung, cách thiết kế tốt nhất quyết định truy cập những phương thức của
giao diện thông qua tham chiếu của giao diện. Do vậy cách tốt nhất sử dụng
hinh.InThongTin() hơn sử dụng h.InThongTin() trong dụ trước. Truy cập thông
qua giao din cho phép chúng ta đối x giao diện mt cách đa nh. Nói cách khác,
chúng ta tạo hai hay nhiều hơn nhng lớp thực thi giao din, sau đó bằng cách truy
cập lớp này chỉ thông qua giao diện.
Gán đối tượng cho một giao din
Trong nhiều trường hợp, chúng ta không biết trước mt đối tượng hỗ trợ
mt giao din đưa ra. Giả sử ta viết thêm một giao din Ithu nào đó nhưng lp
HinhTron của chúng ta không trin khai giao diện này t po gán sau sẽ không hợp lệ
Ithu t=(IThu)h;
Khi biên dịch chương trình thì dòng lnh trên không hề bị báo lỗi Ithu mt giao
diện hoàn toàn hợp lệ, Tuy nhiên khi chạy chương trình sẽ tạo ra một ngoại lệ
(exception). Và chương trình dừng li.
Giao diện đối lập với lớp trừu tượng
Giao din rất ging như các lớp trừu tượng. Thật vậy, chúng ta thể thay thế
khai o của IHinh trthành mt lớp trừu tượng:
HinhTron h = new HinhTron(4, 7, 4);
abstract class IHinh
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 111
Bây giờ lớp HinhChuNhat ththừa kế tlớp trừu tượng IHinht, cũng không
gì khác nhiều so với việc sử dụng giao diện.
Tuy nhiên, giả sử chúng ta mua một lớp ClsDaGiac t một hãng thứ ba chúng ta
muốn kết hợp với lớp sẵn như IHinh. Tuy nhiên trong nn ngữ C# chúng ta
không cho phép thực hin đa kế thừa.
Tuy nhiên, ngôn ngữ C# cho phép chúng ta thực thi bất cứ những giao din nào
dẫn xuất t mt lớp sở. Do đó, bằng cách làm cho IHinh một giao din, chúng
ta có thể kế thừa từ lớp ClsDaGiac và cũng từ giao diện IHinh. Ta thể tạo lp
HinhChuNhat như sau:
8.2.2. Toán tử is
Chúng ta muốn kiểm tra một đi tượng xem hỗ trợ giao diện, để sau đó thực
hiện các phương thức tương ứng. Trong nn ngữ C# hai cách để thực hiện điều
này. Phương pháp đầu tiên sử dụng toán tis.
pháp của toán tử is là:
<biểu thức> is <kiểu dữ liệu>
Toán tử is tr về giá trị true nếu biểu thức thường kiểu tham chiếu thể được gán
an toàn đến kiểu dữ liệu cần kim tra không phát sinh ra bất cứ ngoại lệ nào.
dụ 4.2 minh họa việc sử dụng toán tử is
để kim tra HinhTron
thực thi
giao diện Ihinh hay không
abstract public void ChuVi();
abstract public void DienTich();
}
public class HinhChuNhat: ClsDaGiac, IHinh
{
// Code của lớp
}
class Chay
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 112
Trong dụ 4.2, hàm Main() lúc này sẽ thực hin việc gán với interface khi được
kiểm tra hợp lệ. Việc kiểm tra này được thực hiện bởi câu lệnh if:
if ( doc is IStorable )
Biểu thức điều kiện sẽ trả về giá trị true phép gán sẽ được thực hiện khi đối tượng
có thực thi giao diện bên phải của toán tử is.
Tuy nhiên, việc sử dụng toán tis đưa ra một việc không hiệu quả. Để hiểu được
điều này, chúng ta xem đoạn chương trình được biên dch ra mã IL. đây sẽ mt
ngoại lệ nhỏ, các dòng n dưới là sử dụng hệ thập lục phân:
{
static void Main()
{
HinhTron h = new HinhTron(4, 7, 4);
// Phép gán này không an toàn có thgây li cho chương tnh nếu lớp HinhTron //không
thực thi giao diện IHinh
IHinh hinh1 = (IHinh)h;
if (h is IHinh)
{
IHinh hinh = (IHinh)h;
// Phép gán này an toàn
hinh.InThongTin();
Console.WriteLine("Hinh thuc thi IHinh");
}
else
h.InThongTin();
Console.Read();
}
}
IL_0023: isinst
IHinh
IL_0028: brfalse.s IL_0039
IL_002a: ldloc.0
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 113
Điều quan trọng xảy ra khi phép kiểm tra IHinh ng if (h is IHinh)
”. Từ khóa isinst MSIL tương ng với toán tử is. Nếu việc kiểm tra đối tượng
(h) đúng kiểu của kiểu bên phi. Thì chương trình sẽ chuyển đến dòng lệnh tiếp theo
để thực hin tiếp và castclass được gọi. Điều không may castcall ng kim tra kiu
của đối tượng. Do đó việc kim tra sẽ được thực hiện hai lần. Gii pháp hiệu quả hơn
việc sử dụng toán t as.
8.2.3. Toán tử as
Toán t as kết hợp toán t is phép gán bằng cách đầu tiên kiểm tra hợp lệ
phép n (kiểm tra toán t is trả về true) rồi sau đó phép gán được thực hiện. Nếu phép
gán không hợp lệ (khi phép gán trả về giá trị false), thì toán t as trả về giá trị null.
Ghi chú: Từ khóa null thể hiện mt tham chiếu không tham chiếu đến đâu c
(null reference). Đối tượng có giá trị null tức là không tham chiếu đến bất kỳ đối tượng
o.
Sử dụng toán tử as để loi bỏ việc thực hiện các xử ngoại lệ. Đồng thời ng
tránh việc thực hin kim tra thừa hai lần. Do vậy, việc sử dụng tối ưu của phép gán
cho giao din là sử dụng as.
pháp sử dụng toán tử as nsau:
<biểu thức> as <kiểu dữ liệu>
Đoạn chương trình sau thay thế việc sử dụng toán tử is bằng toán tử as và sau đó
thực hin việc kiểm tra xem giao diện được gán có null hay không:
IL_002b: castclass IHinh
IL_0030: stloc.2
IL_0031: ldloc.2
IL_0032: callvirt instance void IHinh::InThongTin()
IL_0037: br.s
IL_0043
IL_0039: ldstr
“Compressible not supported
static void Main()
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 114
Ta thể so sánh đoạn mã IL sau với đoạn IL sử dụng toán t is trước sẽ thấy
đoạn mã sau có nhiều hiệu quả hơn:
Ghi chú: Nếu mục đích của chúng ta là kiểm tra một đối tượng htrợ một giao
diện sau đó thực hin việc gán cho một giao din, t cách tốt nhất sử dụng
toán tử as hiệu quả nhất. Tuy nhiên, nếu chúng ta chỉ muốn kim tra kiểu dữ liệu
không thực hiện phép gán ngay lúc đó. lẽ chúng ta chỉ muốn thực hiện việc
kiểm tra nhưng không thực hiện việc gán, đơn gin chúng ta muốn thêm vào
danh sách nếu chúng thực sự một giao diện. Trong trường hợp này, sử dụng toán t
is là cách lựa chn tốt nhất.
8.3. Thc thi giao din
8.3.1. Thực thi giao diện
Khi thực thi một lớp chúng ta thể tự do đánh dấu bất kỳ hay tất cả các
phương thức thực thi giao diện như mt phương thức ảo. dụ, lớp HinhChuNhat
HinhTron h = new HinhTron(4, 7, 4);
IHinh hinh = h as IHinh;
if (h != null)
{
hinh.InThongTin();
Console.WriteLine("Hinh thuc thi IHinh");
}
else
h.InThongTin();
Console.Read();
}
IL_0023: isinst
IHinh
IL_0028: stloc.2
IL_0029: ldloc.2
IL_002a: brfalse.s IL_0034
IL_002c: ldloc.2
IL_002d: callvirt
instance void IHinh::InThongTin()
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 115
thực thi giao diện IHinh thể đánh dấu InThongTin() như phương thức ảo.
Những nời phát triển sau thdẫn xuất mt kiểu dữ liệu mi từ lớp HinhTron, có
th lớp HinhVuong, những người y mong muốn lớp HinhVuong thực thi
phương thức InThongTin() riêng của lớp nh vuông.
dụ 4.3
using System;
namespace LTHDTC
{
//Xây dựng giao diện IHinh
interface IHinh
{
string TenHinh
{
get;
}
double ChuVi();
double DienTich();
void InThongTin();
}
//Lớp HinhChuNhat trin khai giao diện IHinh
class HinhChuNhat : IHinh
{
protected double chieuDai, chieuRong;
// Thực thi phương thức của giao diện
public HinhChuNhat(double chieuDai, double chieuRong)
{
this.chieuDai = chieuDai;
this.chieuRong = chieuRong;
}
// Thực thi phương thức của giao diện và khai báo phương thức là phương //thức ảo,
để các lớp kế thừa lớp có thtự do thực thi phương thức của riêng mình/
public virtual void InThongTin()
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 116
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh,
DienTich(), ChuVi());
}
public double ChuVi()
{
return 2 * (chieuRong + chieuDai);
}
public double DienTich()
{
return chieuDai * chieuRong;
}
public virtual string TenHinh
{
get { return "Hinh chu nhat"; }
}
}
// Lớp HinhVuong kế thừa t HinhChuNhat có thể ghi đè các phương thức ảo của //lớp
cơ sở
class HinhVuong : HinhChuNhat
{
public HinhVuong(double canh)
: base(canh, canh)
{ }
public override string TenHinh
{
get
{
return "Hinh Vuong";
}
}
// Ghi đè phương thức ảo của lớp cơ sở
public override void InThongTin()
{ base.InThongTin();
Console.WriteLine("Day la lop hinh vuong da ghi de");
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 117
Do lớp HinhVuong kế thừa từ HinhChuNhat, mà HinhChuNhat li trin khai giao din
Ihinh, nên để thực thi phương thức InThongTin ta có 4 cách, đó là những cách nào?
+ Trực tiếp từ đi tượng HinhVuong:
HinhVuong hv = new HinhVuong(4);
hv.InThongTin();
+ Thông qua đối tượng HinhChuNhat:
HinhChuNhat hv = (HinhChuNhat)new HinhVuong(4);
hv.InThongTin();
+ Thông qua giao diện mà lớp cơ sở của nó trin khai
HinhVuong hv = new HinhVuong(4);
IHinh ihv = hv as IHinh;
ihv.InThongTin();
+ Thông qua giao diện mà lớp cơ sở của nó triển khai
HinhChuNhat h;
HinhVuong hv = new HinhVuong(4);
h=hv;
}
}
class Chay
{
static void Main()
{
IHinh hv = new HinhVuong(4) as IHinh;
if (hv != null)
{ hv.InThongTin(); }
Console.Read();
}
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 118
IHinh ihv = h as IHinh;
ihv.InThongTin();
Thực thi giao diện tường minh
Trong việc thực thi giao din cho tới giờ, những lớp thực thi tạo ra các phương
thức thành viên ng ký hiệu và kiểu trả về như là phương thức được mô t trong giao
diện. Chúng ta không cần thiết khai báo tường minh rằng đây mt thực thi của một
giao din, việc này được hiểu ngầm bởi trình biên dch.
Tuy nhiên, vấn đề xảy ra khi mt lớp thực thi hai giao din cả hai giao din này
các phương thức cùng mt hiệu. Ví dụ 4.4 tạo ra hai giao diện: IHinh IDaGiac.
Sau đó thực thi phương thức InThongTin() trong giao diện IDiem phương thức
InThongTin() để in thông tin về đa giác. Không may phương thức này sẽ tranh chấp
với phương thức InThongTin () của IHinh mà phải thực thi.
Bởi cả hai giao din cùng có một phương thc InThongTin,vic thực thi lớp
HinhChuNhat phải sử dụng thực thi tường minh cho mi phương thức. Với việc thực
thi tường minh, lớp thực thi HinhChuNhat sẽ khai báo tường minh cho mi phương
thức:
void IHinh.InThongTin ();
Điều này sẽ giải quyết việc tranh chấp, nhưng sẽ tạo ra hàng loạt các hiệu ứng thú vị.
+ Với các phương thức khác thì không cần thiết phi sử dụng thực thi tường minh
không có sự tranh chấp cho nên ta khai báo như thông thường.
+ Phương thức thực thi tường minh không bổ sung truy cập:
void IHinh.InThongTin (); phương thức này được hiểu ngầm định là public
Thật vậy, một phương thức được khai báo tường minh thì sẽ không được khai báo vi
các từ khóa bổ sung truy cập: abstract, virtual, override, và new.
+ Chúng ta không thể truy cập phương thức thực thi tường minh thông qua chính
đối tượng,
HinhChuNhat h=new HinhChuNhat(3,4);
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 119
h.InThongTin();
Trình biên dch sẽ báo li
Khi đó muốn gọi phương thức InThongTin thì ta phải gán biến đối tượng HinhChuNhat
cho một đi tượng của giao
Sử dụng thực thi tường minh được áp dụng trong dụ 4.4
dụ 4.4: Thực thi tường minh.
Error 1
'LTHDTC.HinhChuNhat' does not contain a definition for 'InThongTin'
and no extension method 'InThongTin' accepting a first argument of type
'LTHDTC.HinhChuNhat' could be found (are you missing a using directive or an
assembly reference?) C:\Tai lieu giang day\Lap trinh huong doi tuong\Vi
du\LTHDTC\LTHDTC\clsHinh.cs 171 15 LTHDTC
static void Main()
{
HinhChuNhat h = new HinhChuNhat(3, 4);
h.InThongTin();
IDaGiac d = h as IDaGiac;
if (d != null)
{
d.InThongTin();
}
Console.Read();
}
using System;
namespace LTHDTC
{
interface IDaGiac
{
void InThongTin();
}
interface IHinh
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 120
{
string TenHinh
{
get;
}
double ChuVi();
double DienTich();
void InThongTin();
}
class HinhVuong : HinhChuNhat
{
public HinhVuong(double canh)
: base(canh, canh)
{ }
public override string TenHinh
{
get
{
return "Hinh Vuong";
}
}
public void InThongTin()
{
Console.WriteLine("Day la lop hinh vuong da ghi de");
}
}
class HinhChuNhat : IHinh, IDaGiac
{
protected double chieuDai, chieuRong;
public HinhChuNhat(double chieuDai, double chieuRong)
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 121
this.chieuDai = chieuDai;
this.chieuRong = chieuRong;
}
// Phương thức thực thi tường minh của giao din IHinh
void IHinh.InThongTin ()
{
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh,
DienTich(), ChuVi());
}
// Phương thức thực thi tường minh của giao diện IDaGiac
void IDaGiac.InThongTin()
{
Console.WriteLine("{0} tứ giác các góc bằng 90 độ",TenHinh );
}
public double ChuVi()
{
return 2 * (chieuRong + chieuDai);
}
public double DienTich()
{
return chieuDai * chieuRong;
}
public virtual string TenHinh
{
get { return "Hinh chu nhat"; }
}
}
}
class Chay
{
static void Main()
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 122
Lựa chọn việc thể hiện phương thức giao diện
Những nời thiết kế lớp thể thu được li khi mt giao diện được thực thi thông
qua thực thi tường minh không cho phép các thành phn client của lớp truy cập
trừ phi sử dụng thông qua việc gán cho giao diện.
Gi sử nghĩa của đi tượng HinhChuNhat chỉ ra rằng thực thi giao din
IHinh, nhưng không muốn phương thức InThongTin() phần giao diện public
của lớp HinhChuNhat. Chúng ta thể sử dụng thực thi tường minh để chắc chắn
chỉ th truy cập thông qua việc gán cho giao din. Điều này cho phép chúng ta
lưu trữ ngữ nghĩa của lớp HinhChuNhat trong khi vẫn thể thực thi được giao
diện IHinh. Nếu thành phần client muốn đối tượng thực thi giao diện IHinh,
th thực hiện n tường minh cho giao din để gọi các phương thức thực thi giao
diện. Nhưng khi s dụng đối tượng HinhChuNhat t nghĩa là không phương thức
InThongTin().
Thật vậy, chúng ta thể lựa chọn thể hin những phương thức thông qua
thực thi tường minh, do đó chúng ta thể trưng y một vài phương thức thực thi
như một phần của lớp HinhChuNhat một số phương thức khác t không.
Trong dụ 4.4, đối tượng HinhChuNhat trưng bày phương thức ChuVi(),
DienTich() như là phương thức của lớp HinhChuNhat, nhưng phương thức
InThongTin() chỉ được thể hin thông qua gán cho giao din. Thậm chí nếu
IHinh không phương thức InThongTin(), chúng ta cũng th chọn thực thi
tường
minh phương thức InThongTin() để phương thức không được thể hin ra bên
HinhChuNhat h = new HinhChuNhat(3, 4);
h.InThongTin();
IDaGiac d = h as IDaGiac;
if (d != null)
{
d.InThongTin();
}
Console.Read();
}
}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 123
ngoài như các phương thức của HinhChuNhat.
Chúng ta lưu ý rằng thực thi giao diện tường minh nn ngừa việc sử dụng t
khóa virtual, mt lớp dẫn xuất thể được hỗ trợ để thực thi lại phương thức. Do
đó, nếu HinhVuong dẫn xuất từ HinhChuNhat, thể được thực thi li phương
thức HinhChuNhat.InThongTin () bởi lớp HinhChuNhat thực thi phương thức
InThongTin() không phải ảo.
n thành vn
Ngôn ngữ C# cho phép ẩn c thành viên của giao din. dụ, chúng ta mt giao
diện
IBase với mt thuộc tính P:
sau đó chúng ta dẫn xuất từ giao diện này ra mt giao din kc, IDerived, giao
diện mi này làm ẩn thuộc tính P với mt phương thức mới P():
Việc i đặt này mt ý tưởng tốt, bây gi chúng ta thể ẩn thuộc tính P trong lớp
sở. Một thực thi của giao diện dẫn xuất y đòi hi ti thiểu mt thành viên
giao din tường minh. Chúng ta thể sử dụng thực thi tường minh cho thuộc tính
của lớp cơ sở hoặc của phương thức dẫn xuất, hoặc chúng ta thể sử dụng thực thi
tường minh cho cả hai. Do đó, ba phiên bn được viết sau đều hợp lệ:
interface IBase
{
int P { get; set;}
}
interface IDerived : IBase
{
new int P();
}
class myClass : IDerived
{
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 124
8.3.2. Mở rộng giao diện
C# cung cấp chức năng cho chúng ta m rộng mt giao din đã bằng cách
thêm các phương thức các thành viên hay bổ sung cách làm việc cho các thành viên.
dụ, chúng ta có thể mở rộng giao din IHinh với một giao din mới IHinhs.
Giao din mới này mở rộng giao diện bng cách thêm phương thức Tinhchat
// thực thi tường minh cho thuộc tính cơ sở
int IBase.p { get{...}}
// thực thi ngầm định phương thức dẫn xuất
public int P() {...}
}
class myClass : IDerived
{
// thực thi ngầm định cho thuộc tính cơ sở
public int P { get{...}}
// thực thi tường minh phương thức dẫn xuất
int IDerived.P() {...}
}
class myClass : IDerived
{
// thực thi tường minh cho thuộc tính cơ sở
int IBase.P { get{...}}
// thực thi tường minh phương thức dẫn xuất
int IDerived.P(){...}
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 125
Các lớp khác thể thực thi tự do giao diện IHinh hay Ihinhs tùy thuộc vào mục
đích cần thêm chức ng hay không. Nếu một lớp thực thi giao din IHinhs,
t lớp này phải thực thi tất cả các phương thức của cả hai giao diện IHinh giao
diện IHinhs. Những đối tượng của lớp thực thi giao diện IHinhs thể được n
cho cả hai giao diện IHinhs và IHinh.
Kết hợp các giao diện
Một cách tương tự, chúng ta thể tạo giao diện mới bằng ch kết hợp các
giao diện cũ ta thể thêm các phương thức hay các thuộc tính cho giao diện mi.
dụ, chúng ta quyết định tạo một giao diện IhinhDaGiac Giao din mới này s kết
hợp những phương thức của cả hai giao din IHinh IDa Giac ng tm
o một phương thức mi So Canh().
Khi đó nếu lớp nào lớp nào triển khai giao din IhinhDaGiac t sẽ phải trin khai tất cả
các phương thức, thuộc tính của cả hai giao din IHinh và IdaGiac và phương thức b
sung SoCanh.
8.3.3. Tham chiếu đối tượng của lớp con qua giao diện
Như đã trình bày trong các mục trước, chúng ta thường sử dụng giao din để thực thi
các phương thức của đi tượng lớp triển khai từ giao din, điều đó giúp chúng ta đa
hình.
Interface
Ihinhs:IHinh
{
Void TinhChat();
}
inter face Ihinh Da Giac: IHinh, IDa Giac
{
int So Canh();
}
Class chay
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 126
8.3.4. Giao diện đối lập với lớp trừu ợng
Giao din rất ging như các lớp trừu tượng. Thật vy, chúng ta thể thay thế
khai o của IStorable trở thành mt lớp trừu tượng:
Bây giờ lớp Document thể thừa kế tlớp trừu tượng IStorable, ng không
khác nhiều so vi việc sử dụng giao diện.
Tuy nhiên, giả sử chúng ta mua một lớp List từ một hãng thứ ba chúng ta muốn
kết hợp với lớp sẵn như Storable. Trong ngôn ngữ C++ chúng ta thể tạo ra một
lớp StorableList kế thừa từ List cả Storable. Nhưng trong ngôn ngữ C# chúng
ta không thể làm được, chúng ta không thể kế thừa từ lớp trừu tượng Storable t
lớp List bởi trong C# không cho phép thực hiện đa kế thừa từ những lớp.
Tuy nhiên, ngôn ngữ C# cho phép chúng ta thực thi bất cứ những giao din nào
dẫn xuất từ một lớp sở. Do đó, bng cách làm cho Storable mt giao diện,
chúng ta thể kế thừa từ lớp List cũng từ IStorable. Ta thể to lớp StorableList
{
static void ThaoTacHinh(IHinh i)
{
i.InThongTin();
}
static void Main()
{
ThaoTacHinh(new HinhChuNhat(3,4));
ThaoTacHinh(new HinhTron(3,6,8));
ThaoTacHinh(new HinhVuong(7));
}
}
abstract class Storable
{
abstract public void Read();
abstract public void Write();
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 127
như sau:
8.4 Kỹ thut xử ngoi lệ
8.4.1 Khái niệm xử ngoại lệ
a.
Đón bắt lỗi với try catch
Xét đoạn chương trình sau:
Chương trình trên khi dịch chạy chương trình sẽ chẳng vấn đề xảy ra, nếu
người s dụng nhập đúng giá trị cho biến i. Nhưng đoạn chương trình trên sẽ bị tạm
ngừng không chạy được nếu người sử dụng không nhập vào một giá tr số mà nhập vào
mt chữ i ‘a’ chẳng hạn. Khi đó li phát sinh của chương trình li nguyên nhân
do phía hình động của người sdụng làm phát sinh mt ngoại lchứ không phải li
lập trình.
Ngôn ngữ C# cũng giống như bất cứ ngôn ngữ hướng đối tượng khác, cho phép
public class StorableList : List, IStorable
{
// phương thức List........
public void Read()
{...}
public void Write( object o)
{...}
//....
}
static void Main()
{
int i;
Console.Write("Nhap gia tri cho i: ");
i = int.Parse(Console.ReadLine());
Console.WriteLine("Giá tri i vừa nhập " + i);
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 128
xử những li các điều kin không bình thường với nhng ngoại lệ. Ngoại lệ là
mt đối tượng đóng i những thông tin về sự cố của mt chương trình không bình
thường.
Một điều quan trọng để phân chia giữa bug, li, ngoại lệ. Một bug mt li
lập trình thể được sửa chữa trước khi nguồn được chuyển giao. Những ngoại lệ
t không được bảo vệ tương phn với những bug. Mặc mt bug thể là nguyên
nhân sinh ra ngoại lệ, chúng ta cũng không dựa vào những ngoi lệ để xử những
bug trong chương trình, tốt hơn chúng ta nên sửa chữa những bug này.
Khi chúng ta thực hiện chương trình thì th những ngoi lệ phát sinh trong
quá trình chy do mt tác động nào đó mà nguyên nhân không phải li lập tnh, những
li này với người lập trình kinh nghiệm họ thể dự đoán trước được nhng li phát
sinh khi chạy chương trình. Khi mt ngoại lệ được tạo ra, việc thực thi của các chức
năng hin hành sẽ bị treo cho đến khi nào việc xử lý ngoại lệ tương ứng được tìm thy.
Điều này nghĩa rằng nếu chức năng hoạt động hiện hành không thực hiện việc
xử ngoại lệ, t chc năng này sẽ bị chấm dứt hàm gọi sẽ nhận sự thay đổi
đến việc xử ngoại lệ. Nếu hàm gọi này không thực hiện việc xử ngoại lệ, ngoại
lệ sẽ được xử sm bởi CLR, điều này dẫn đến chương trình của chúng ta sẽ kết thúc.
Một tnh xử ngoại lệ một khối lệnh chương tnh được thiết kế xử các ngoại
lệ chương trình phát sinh. Xử ngoại lệ được thực thi trong trong câu lệnh catch.
Một cách tưởng t nếu mt ngoại lđược bắt được xử , t chương trình thể
sửa chữa được vn đề tiếp tục thực hin hoạt động. Thậm chí nếu chương trình
không tiếp tục, bằng việc bắt gingoại lchúng ta hội để in ra nhng thông đip
ý nghĩa và kết thúc chương trình mt cách rõ ràng.
Cấu trúc xử dụng khối try …catch để xử ngoại lệ như sau:
try
{
// Đoạn chương trình khả năng phát sinh ngoại lệ trong quá trình chạy
}
Catch (KieuNgoaiLe bien)
{
// Lệnh xử khi đoạn lnh bị phát sinh li chương trình sẽ chuyển sang //thực
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 129
dụ 25.1 xử bắt ngoại lệ bằng try…catch
b.
Dọn dẹp ngoài lệ với try finally
Trong một số tình huống, vic phát sinh ngoi lệ và unwind stack có thể tạo ra một
số vấn đề. dụ như nếu chúng ta mở mt tập tin hay trường hợp khác xác nhận
hiện các câu lệnh đây/
}
Catch (KieuNgoaiLe bien)
{
// Lệnh xử khi đoạn lệnh bị phát sinh li chương trình sẽ chuyển sang //thực
hiện các câu lệnh ở đây/
}
Finally
{
// Lệnh xử cho ngoại lệ hay không ngoại lệ phát sinh.
}
static void Main()
{
try
{
int i;
Console.Write("Nhap gia tri cho i: ");
i = int.Parse(Console.ReadLine());
Console.WriteLine("Giá tri i vừa nhập " + i);
}
catch
{
Console.WriteLine("Co loi phat sinh");
}
Console.ReadLine();
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 130
mt tài nguyên, chúng ta thể cần thiết mt hi để đóng mt tập tin hay giải
phóng bộ nhớ đệm mà chương trình đã chiếm giữ trước đó.
Ghi chú: Trong ngôn ngữ C#, vấn đề này ít xy ra hơn do chế thu dọn tđộng của
C#
ngăn ngừa những ngoi lệ phát sinh từ việc thiếu bộ nhớ.
Tuy nhiên, một số hành động mà chúng ta cần phải quan tâm bất cứ khi nào một
ngoại lệ được phát sinh ra, như vic đóng một tập tin, chúng ta hai chiến lược để
lựa chọn thực hiện. Một hướng tiếp cận đưa hành động nguy hiểm vào trong khối try
sau đó thực hiện việc đóng tập tin trong cả hai khối catch try. Tuy nhiên, điều
này y ra đoạn chương trình không được đẹp do sử dụng trùng lặp lnh. Ngôn ngữ
C# cung cấp mt sự thay thế tốt hơn trong khối finally.
Đoạn chương trình n trong khi catch được đảm bảo thực thi không quan
tâm đến việc khi nào t mt ngoại lđược phát sinh. Phương thức TestFunc() trong ví
dụ 5.2 minh ha việc mở mt tập tin như hành đng đầu tiên của nó, sau đó phương
thức thực hin mt vài các phép toán toán hc, sau đó là tập tin được đóng. thể
trong quá trình mở tập tin cho đến khi đóng tập tin chương trình phát sinh ra mt
ngoại lệ. Nếu xuất hiện ngoi lệ, khi đó tập tin vẫn còn mở. Người phát triển biết
rằng không chuyện xảy ra, cuối của phương thức này thì tập tin sẽ được
đóng. Do chức năng đóng tập tin được di chuyển vào trong khối finally, đây nó sẽ
được thực thi mà không cần quan tâm đến việc phát sinh hay không mt ngoại l
trong chương trình.
dụ 5.2: Sử dụng khối finally.
class NgoaiLe
{
public void MoTep()
{
StreamWriter f = new StreamWriter(new FileStream("ketqua.txt",
FileMode.Append));
try
{
int x, y;
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 131
Console.Write("Gia tri x: "); x = int.Parse(Console.ReadLine());
Console.Write("Gia tri y: "); y = int.Parse(Console.ReadLine());
double z = (double)x / y;
Console.Write("Nhap vao mot so: ");
int so = int.Parse(Console.ReadLine());
f.WriteLine("{0}/{1}={2}", x, y, z);
//f.Close();
}
// Các câu lnh đặt trong khối catch được xử khi có ngoại lệ xảy ra.
catch(Exception ex)
{
Console.WriteLine(ex.Message);
//f.Close();
}
// Các câu lnh đặt trong khối finally sẽ được xử lý dù có hay không có ngoại lệ pt
sinh.
Finally
{
f.Close();
}
StreamReader fd = new StreamReader(new FileStream("ketqua.txt",
FileMode.Open));
Console.WriteLine(fd.ReadToEnd());
fd.Close();
Console.ReadLine();
}
}
class chay
{
static void Main()
{
NgoaiLe f = new NgoaiLe();
f.MoTep();
}
Đ
ơng Lập tnh ớng đối ợng vi C#
Trang 132
c.
Kiểm soát tất cả ngoại lệ với try-catch-finally
Với một đoạn chương trình thxảy ra nhiều ngoại lkhác nhau tùy vào từng trường
hợp, chúng ta muốn ứng xử khác nhau tùy vào tng trường hợp khác nhau khi đó chúng
ta sử dụng nhiều khối catch. Mỗi một khối catch bắt một kiểu ngoại lệ khác nhau, lưu
ý rằng kiểu ngoi lcha phải đặt sau ngoại lcon. Các câu lệnh được xử hay
không có ngoại lệ xảy ra chúng ta đặt trong khi finally.
dụ 5.3:
}
public void NhapSo()
{
int x, y,z=0;
try
{
Console.Write("Nhap gia tri x: ");
x = int.Parse(Console.ReadLine());
Console.Write("Nhap gia tri y: ");
y = int.Parse(Console.ReadLine());
z = x / y;
}
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message );
Console.WriteLine("gia tri mac dinh z =0");
z = 0;
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message );
Console.WriteLine("Gia tri mac dinh cua z=1");
z = 1;
}
finally
8.4.2. c lp ngoi lệ ca .Net
Đối tượng System.Exception cung cấp mt số các phương thức thuộc tính hữu
dụng. Thuộc tính Message cung cấp thông tin về ngoại lệ, như là lý do tại sao ngoại l
được phát sinh. Thuộc tính Message thuộc tính chỉ đọc, đoạn chương trình phát
sinh ngoại lệ thể thiết lập thuộc tính Message như mt đối mục cho bộ khởi
dựng của ngoi lệ. Thuộc tính HelpLink cung cấp mt liên kết để trợ giúp cho các tập
tin liên quan đến các ngoại lệ. Đây thuộc tính chỉ đọc. Thuộc tính StackTrace cũng
thuộc tính chỉ đọc và được thiết lập bi CLR. Trong dụ 5.4 thuộc tính
Exception.HelpLink được thiết lập và truy cập để cung cấp thông tin cho người sử
dụng về ngoại lệ DivideBy-ZeroException. Thuộc tính StackTrace của ngoại lđược
sử dụng để cung cấp thông tin stack cho câu lệnh li. Một thông tin stack cung cấp
hàng loạt các cuộc gọi stack của phương thức gọi dẫn đến những ngoại lđược
phát sinh.
Bảng sau tả mt số các lớp ngoại lệ chung được khai báo bên trong
namespace System.
CÁC LỚP NGOẠI LỆ
Tên ngoại lệ
tả
MethodAccessException
Lỗi truy cập, do truy cập đến thành viên
hay phương thức không được truy cập
ArgumentException
Lỗi tham số đối mục
ArgumentNullException
Đối mục Null, phương thức được truyn đối
mục null không được chấp nhận.
ArithmeticException
Lỗi liên quan đến các phép toán
{
Console.WriteLine("z= " +z);
}
Console.ReadLine();
}
ArrayTypeMismatchException
Kiểu mảng không hợp, khi cố lưu trữ kiểu
không tch hợp vào mảng
DivideByZeroException
Lỗi chia zero
FormatException
Định dạng không chính c một đối mc nào đó
IndexOutOfRangeException
Chỉ số truy cập mảng không hợp lệ, dùng nh
hơn chỉ số nh nhất hay lớn n chỉ số lớn
nhất của mng
InvalidCastException
Phép gán không hợp lệ
MulticastNotSupportedException
Multicast không được hỗ trợ, do việc kết hợp
hai delegate không đúng
NotFiniteNumberException
Không phi số hữu hạn, số không hợp lệ
NotSupportedException
Phương thức không hỗ trợ, khi gọi một
phương thức không tồn ti bên trong lớp.
NullReferenceException
Tham chiếu null không hợp lệ.
OutOfMemoryException
Out of memory
OverflowException
Lỗi tràn phép toán
StackOverflowException
Tràn stack
TypeInitializationException
Kiểu khởi tạo sai, khi bộ khởi dựng tĩnh li.
8.4.3 Ném ngoi lệ
a.
Ném lại những ngoại lệ
Khi mt ngoại lxảy ra chúng ta muốn phát sinh ngoại lệ ra bên ngoài khối
cacth (trong một hàm gọi) thì khi đó chúng ta cần ném ngoi lệ ra ngoài khối catch,
pháp để thực hiện phát sinh lại cùng mt ngoại lệ không bất cứ bổ sung hay
hiệu chỉnh o là : throw bienNgoaiLe;
dụ 5.4
class NgoaiLe
{
public int Chia()
{
int x, y,z=0;
try
{
Console.Write("Nhap gia tri x: ");
x = int.Parse(Console.ReadLine());
Console.Write("Nhap gia tri y: ");
y = int.Parse(Console.ReadLine());
z = x / y;
}
catch (Exception ex)
{
// Ném ngoại lệ ra ngoài khối cacth
Exception x = new Exception(ex.Message);
throw x;
// Hoặc
//throw new Exception(ex.Message);
}
return z;
}
}
class chay
{
static void Main()
{
NgoaiLe f = new NgoaiLe();
try
{//Phương thức Chia() của lớp Ngoại lệ có thể phát sinh ngoi lệ do trong khi
xây //dựng phương thức chúng ta đã ném ngoại lệ ra ngoài khối catch.
Console.WriteLine ( f.Chia());
}
catch (Exception ex)
{ Console.WriteLine(ex.Message);
b.
Tạo đối ợng ngoại lệ cho ứng dụng
CLR cung cấp những kiểu dữ liệu ngoại lệ bản, trong dụ trước chúng ta đã
to một vài các kiểu ngoại lệ riêng. Thông thường chúng ta cần thiết phi cung cấp
các thông tin mrộng cho khối catch khi mt ngoại lệ được phát sinh. Tuy nhiên,
những lúc chúng ta muốn cung cấp nhiều thông tin mrộng hay các khả năng
đặc biệt cần thiết trong ngoại lệ chúng ta tạo ra. Chúng ta dễ dàng tạo ra các ngoại
lệ riêng, hay còn gọi các ngoại lệ tùy chọn (custom exception), điều bắt buộc vi
các ngoại lệ y chúng phải được dẫn xuất từ System.ApplicationException. dụ
5.5 sau minh họa việc tạo mt ngoại lệ riêng.
dụ 5.5
}
}
class ClsNgoaile : System.ApplicationException
{
public ClsNgoaile(string message) : base(message)
{
}
}
class HinhTront
{
public int x, y, r;
public HinhTront(int x, int y, int r)
{
if (r < 0)
throw new ClsNgoaile("So Khong duoc am");
else
this.x = x;
this.y = y;
this.r = r;
}
}
class test
{
static void Main()
{
try {
HinhTront t = new HinhTront(4, 3, -4);
} catch (ClsNgoaile ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
| 1/137

Preview text:

Đề cương Lập trình hướng đối tượng với C# Trang 1 MỤC LỤC
MỤC LỤC ..................................................................................................... 1
Bài 1: Các khái niệm cơ bản về lập trình hướng đối tượng ....................... 4
1.1. Lịch sử phát triển của phương pháp lập trình .................................................................................... 4
1.1.1 Lập trình không có cấu trúc (hay lập trình tuyến tính) ................................................................. 4
1.1.2 Lập trình hướng chức năng(lập trình cấu trúc) ............................................................................ 5
1.1.3 Lập trình module ........................................................................................................................ 6
1.1.4 Lập trình hướng đối tượng .......................................................................................................... 8
1.2 Một số khái niệm trong lập trình hướng đối tượng ............................................................................. 9
1.2.1 Trừu tượng hóa dữ liệu (Data Abstraction).................................................................................. 9
1.2.2 Lớp (Class) ............................................................................................................................... 12
1.2.3 Đối tượng (Object).................................................................................................................... 14
1.2.4 Thuộc tính ................................................................................................................................ 16
1.2.5 Hoạt động (Operation) .............................................................................................................. 17
1.2.6 Phương thức (Method) .............................................................................................................. 17
1.2.7 Thông điệp (Message)............................................................................................................... 18
1.2.8 Sự kiện (Event) ......................................................................................................................... 19
1.2.8 Phân biệt giữa lớp và đối tượng ................................................................................................ 19
1.2.9 Thiết lập (Construction) và Hủy (Destruction) .......................................................................... 19
1.2.10 Tính Bền vững (Persistence) ................................................................................................... 21
1.2.11 Tính Đóng gói dữ liệu ............................................................................................................. 22
1.2.12 Tính thừa kế ........................................................................................................................... 23
1.2.13 Tính Đa Thừa kế..................................................................................................................... 25
1.2.14 Tính Đa hình........................................................................................................................... 26
1.3 Các ưu điểm của lập trình hướng đối tượng ..................................................................................... 28
1.3.1. Các ưu điểm của lập trình hướng đối tượng ............................................................................. 28
1.3.2. Mốt số ngôn ngữ lập trình hướng đối tượng ............................................................................. 28
1.3.3. Ngôn ngữ lập trình C# ............................................................................................................. 30
Bài 2: Lớp và đối tượng(1) ......................................................................... 32
2.1. Lớp ................................................................................................................................................. 32
2.1.1. Định nghĩa lớp ......................................................................................................................... 32
2.1.2 Thành phần dữ liệu ................................................................................................................... 36
Đề cương Lập trình hướng đối tượng với C# Trang 2
2.1.3 Phương thức ............................................................................................................................. 38
2.1.4. Các từ khoá đặc tả truy cập ...................................................................................................... 38
2.1.5. Khai báo đối tượng, mảng đối tượng ........................................................................................ 41
2.1.6. Định nghĩa chồng phương thức ................................................................................................ 42
2.1.7. Thuộc tính và thủ tục thuộc tính .............................................................................................. 44
2.1.8. Từ khoá this ............................................................................................................................. 48
Bài 3: Lớp và đối tượng(2) ......................................................................... 52
3.1. Phép gán các đối tượng .................................................................................................................. 52
3.2. Phương thức thiết lập và sao chép................................................................................................... 52
3.2.1. Phương thức thiết lập .............................................................................................................. 52
3.2.2. Phương thức sao chép .............................................................................................................. 59
3.3. Huỷ bỏ đối tượng, cơ chế gom rác trong .Net ................................................................................ 60
Bài 4: Lớp và đối tượng(3) ......................................................................... 64
4.1. Thành phần tĩnh và cách sử dụng .................................................................................................... 64
4.1.1. Thành phần dữ liệu tĩnh. .......................................................................................................... 64
4.1.2. Phương thức tĩnh ..................................................................................................................... 66
4.2. Lớp bao và cách sử dụng ................................................................................................................ 69
Bài 5: Kế thừa và đa hình(1) ...................................................................... 71
5.1. Giới thiệu chung về thừa kế ............................................................................................................ 71
5.2. Xây dựng lớp dẫn xuất thừa kế từ lớp cơ sở ................................................................................... 74
5.3. Mức truy cập trong lớp dẫn xuất ..................................................................................................... 77
5.4. Gọi phương thức khởi tạo của lớp cơ sở ......................................................................................... 78
5.5. Truy xuất các thành phần của lớp cơ sở .......................................................................................... 79
5.6. Boxing và Unboxing dữ liệu ........................................................................................................... 80
5.7. Các lớp lồng nhau ........................................................................................................................... 82
Bài 6: Kế thừa và đa hình(2) ...................................................................... 85
6.1. Giới thiệu chung về đa hình ............................................................................................................ 85
6.2. Phương thức đa hình....................................................................................................................... 85
6.3. Từ khoá new và override ................................................................................................................ 89
Bài 7: Kế thừa và đa hình(3) ...................................................................... 92
7.1 Lớp trừu tượng ................................................................................................................................ 92
7.1.1. Xây dựng lớp cơ sở trừu tượng ............................................................................................... 92
7.1.2. Kế thừa từ lớp trừu tượng ....................................................................................................... 95
7.1.3. Hạn chế của lớp trừu tượng ..................................................................................................... 99
7.2. Lớp cô lập (sealed class) ............................................................................................................... 100
7.3. Lớp Object ................................................................................................................................... 101
Đề cương Lập trình hướng đối tượng với C# Trang 3
Bài 8: Giao diện ........................................................................................ 104
8.1. Giao diện ...................................................................................................................................... 104
8.1.1. Xây dựng giao diện .................................................................................................................104
8.1.2. Khai báo thuộc tính của giao diện ......................................................................................... 105
8.1.3. Khai báo phương thức của giao diện ...................................................................................... 106
8.2. Thực thi giao diện......................................................................................................................... 107
8.2.1. Thực thi giao diện ...................................................................................................................107
8.2.2. Toán tử is .............................................................................................................................. 111
8.2.3. Toán tử as .............................................................................................................................. 113
8.3. Thực thi giao diện......................................................................................................................... 114
8.3.1. Thực thi giao diện ...................................................................................................................114
8.3.2. Mở rộng giao diện ................................................................................................................ 124
8.3.3. Tham chiếu đối tượng của lớp con qua giao diện ................................................................... 125
8.3.4. Giao diện đối lập với lớp trừu tượng .......................................................................................126
8.4 Kỹ thuật xử lý ngoại lệ .................................................................................................................. 127
8.4.1 Khái niệm xử lý ngoại lệ ........................................................................................................ 127
8.4.2. Các lớp ngoại lệ của .Net ........................................................................................................133
8.4.3 Ném ngoại lệ ......................................................................................................................... 134
Đề cương Lập trình hướng đối tượng với C# Trang 4
Bài 1: Các khái niệm cơ bản về lập trình hướng đối tượng
1.1. Lịch sử phát triển của phương pháp lập trình
Phần này trình bày về một số kỹ thuật hay phương pháp lập trình được phát triển để giải
quyết các vấn đề trong Tin học kể từ khi máy tính ra đời. Sự phát triển của các kỹ thuật
lập trình liên quan chặt chẽ tới sự phát triển phần cứng của máy vi tính cũng như việc
ứng dụng máy tính vào giải quyết các vấn đề trong thực tế. Chúng ta có thể chia các
phương pháp lập trình thành các kiểu sau:
 Lập trình không có cấu trúc
 Lập trình hướng thủ tục
 Lập trình theo kiểu module hóa
 Lập trình hướng đối tượng
Chúng ta sẽ lần lượt xem xét các kỹ thuật lập trình này.
1.1.1 Lập trình không có cấu trúc (hay lập trình tuyến tính)
Thông thường mọi người bắt đầu học lập trình bằng cách viết các chương trình nhỏ và
đơn giản chỉ chứa một “chương trình chính”. Ở đây một chương trình chính có nghĩa là
một tập các lệnh hoặc câu lệnh làm việc với các dữ liệu toàn cục trong cả chương trình
(các biến dùng trong chương trình là các biến toàn cục). Chúng ta có thể minh hoạ bằng hình vẽ sau đây:
Lập trình không có cấu trúc. Chương trình chính thao tác
trực tiếp trên các dữ liệu toàn cục
Một số nhược điểm của lập trình không có cấu trúc:
+ Lập trình không có cấu trúc không có khả năng kiểm soát tính thấy được của dữ
liệu. Mọi dữ liệu trong chương trình đều là biến toàn cục do đó có thể bị thay đổi
bởi bất kỳ phần nào đó của chương trình.
Đề cương Lập trình hướng đối tượng với C# Trang 5
+ Việc không kiểm soát được tính thấy được của dữ liệu dẫn đến các khó khăn
trong việc gỡ lỗi chương trình, đặc biệt là các chương trình lớn.
+ Kỹ thuật lập trình không có cấu trúc có rất nhiều bất lợi lớn khi chương trình đủ
lớn. Ví dụ nếu chúng ta cần thực hiện lại một đoạn câu lệnh trên một tập dữ liệu
khác thì buộc phải copy đoạn lệnh đó tới vị trí trong chương trình mà chúng ta
muốn thực hiện. Điều này làm nảy sinh ý tưởng trích ra các đoạn lệnh thường
xuyên cần thực hiện đó, đặt tên cho chúng và đưa ra một kỹ thuật cho phép gọi và
trả về các giá trị từ các thủ tục này.
1.1.2 Lập trình hướng chức năng(lập trình cấu trúc)
Những ngôn ngữ lập trình truyền thống như: Pascal, C, Foxpro.. được gọi chung là
ngôn ngữ lập trình hướng thủ tục. Theo cách tiếp cận hướng thủ tục thì hệ thống phần
mềm được xem như là dãy các công việc cần thực hiện như đọc dữ liệu, tính toán, xử lý,
lập báo cáo và in ấn kết quả...Mỗi công việc đó sẽ dược thực hiện bởi một hàm hay thủ
tục nhất định. Như vậy trọng tâm của cách tiếp cận này là các hàm chức năng. Nói rõ hơn
theo cách tiếp cận này, chương trình được tổ chức thành các chương trình con. Mỗi
chương trình con đảm nhận xử lý một công việc nhỏ trong toàn bộ hệ thống. Mỗi chương
trình con này lại có thể chia nhỏ thành các chương trình con nhỏ hơn. Quá trình phân chia
như vậy tiếp tục diễn ra cho đến khi các chương trình con nhận được đủ đơn giản. Người
ta gọi đó là quá trình làm mịn dần.
Khi tập trung vào trọng tâm phát triển các hàm thì chúng ta lại ít chú ý đến dữ liệu
( Nghĩa là lập trình hướng thủ tục thì dữ liệu không được coi trọng) những cái mà các
hàm sử dụng để thực hiện công việc của mình.
Cái gì sẽ xẩy ra đối với dữ liệu và gắn dữ liệu với các hàm như thế nào? Trong
chương trình có nhiều hàm, thường thì có nhiều thành phần dữ liệu quan trọng sẽ được
khai báo tổng thể để cho nhiều hàm có thể truy nhập, đọc và làm thay đổi giá trị của biến
tổng thể. Ngoài ra mỗi hàm có thể có các vùng dữ liệu riêng.
Để liên kết giữa các hàm thành một đơn vị chương trình thông qua biến toàn cục.
Đề cương Lập trình hướng đối tượng với C# Trang 6 Dữ liệu chung Dữ liệu chung
Điều này có nghĩa là nhiều hàm có thể truy nhập, sử dụng dữ liệu chung , làm thay
đổi giá trị của chúng và vì vậy rất khó kiểm soát. Nhất là đối với các chương trình lớn,
phức tạp thì vấn đề càng trở nên khó khăn hơn.
Điều gì sẽ xẩy ra nếu như chúng ta muốn thay đổi, bổ xung cấu trúc dữ liệu của
biến toàn cục. Đó là phải viết lại hầu như toàn bộ các hàm có liên quan...
Tóm lại những đặc tính chính của phương pháp lập trình hướng thủ tục là:
+ Tập chung vào công việc cần thực hiện (thuật toán)
+ Chương trình lớn được chia thành các hàm nhỏ hơn
+ Phần lớn các hàm sử dụng dữ liệu chung
+ Dữ liêu trong hệ thống được chuyển động từ hàm này sang hàm khác.
+ Hàm biến đổi dữ liệu từ dạng này sang dạng khác
+ Sử dụng cách tiếp cận top-down trong thiết kế chương trình
1.1.3 Lập trình module
Trong lập trình module các thủ tục có cùng một chức năng chung sẽ được nhóm lại
với nhau tạo thành một module riêng biệt. Một chương trình sẽ không chỉ bao gồm một
phần đơn lẻ. Nó được chia thành một vài phần nhỏ hơn tương tác với nhau qua các lời
gọi thủ tục và tạo thành toàn bộ chương trình.
Đề cương Lập trình hướng đối tượng với C# Trang 7
Lập trình module. Chương trình chính là sự kết hợp giữa các lời
gọi tới các thủ tục trong các module riêng biệt với các dữ liệu thích hợp
Mỗi module có dữ liệu riêng của nó. Điều này cho phép các module có thể kiểm soát các
dữ liệu riêng của nó bằng các lời gọi tới các thủ tục trong module đó. Tuy nhiên mỗi
module chỉ xuất hiện nhiều nhất một lần trong cả chương trình.
Yếu điểm của lập trình thủ tục và lập trình module hóa:
+ Khi độ phức tạp của chương trình tăng lên sự phụ thuộc của nó vào các kiểu dữ liệu
cơ bản mà nó xử lý cũng tăng theo. Vấn đề trở nên rõ ràng rằng cấu trúc dữ liệu sử
dụng trong chương trình cũng quan trọng không kém các phép toán thực hiện trên
chúng. Điều này càng lộ rõ khi kích thước chương trình tăng. Các kiểu dữ liệu được
xử lý nhiều trong các thủ tục của một chương trình có cấu trúc. Do đó khi thay đổi cài
đặt của một kiểu dữ liệu sẽ dẫn đến nhiều thay đổi trong các thủ tục sử dụng nó.
+ Một nhược điểm nữa là khi cần dùng nhiều nhóm làm việc để xây dựng một chương
trình chung. Trong lập trình có cấu trúc mỗi người sẽ được giao xây dựng một số thủ
tục và kiểu dữ liệu. Những lập trình viên xử lý các thủ tục khác nhau nhưng lại có liên
quan tới các kiểu dữ liệu dùng chung nên nếu một người thay đổi kiểu dữ liệu thì sẽ
làm ảnh hưởng tới công việc của nhiều người khác, đặc biệt là khi có sai sót trong
việc liên lạc giữa các thành viên của nhóm.
+ Việc phát triển các phầm mềm mất nhiều thời gian tập trung xây dựng lại các cấu
trúc dữ liệu cơ bản. Khi xây dựng một chương trình mới trong lập trình có cấu trúc
Đề cương Lập trình hướng đối tượng với C# Trang 8
lập trình viên thường phải xây dựng lại các cấu trúc dữ liệu cơ bản cho phù hợp với
bài toán và điều này đôi khi rất mất thời gian.
1.1.4 Lập trình hướng đối tượng
Phong cách của phương pháp lập trình hướng đối tượng (OOP - Object Oriented
Programming) là tập chung vào dữ liệu. Theo cách tiếp cận này thì một câu hỏi thường
được đặt ra là dữ liệu nào thì được các phương thức(hay các hàm) nào xử lý và người ta
đem gói tất cả dữ liệu và các phương thức có liên quan với nhau thành một nhóm giống
như một lọ thuốc bao giờ cũng có hai thứ: Các viên thuốc(dữ liệu) và tờ giấy trên đó ghi
cách dùng các viên thuốc đó(phương thức). Hai thứ đó gộp lại thành một kiểu dữ liệu gọi
là dữ liệu đối tượng(Object). Lập trình có sử dụng kiểu dữ liệu loại này gọi là lập trình hướng đối tượng.
Lập trình hướng đối tượng đặt trọng tâm vào đối tượng. Yếu tố quan trong quá
trình phát triển chương trình và không cho phép dữ liệu biến động tự do trong hệ thống.
Trong một đối tương dữ liệu gắn chặt với các hàm thành viên thành các vùng riêng mà
chỉ có các hàm đó tác động nên và cấm các hàm bên ngoài truy nhập tới tuỳ tiện.
Lập trình hướng đối tượng cho phép phân tích bài toán thành các thực thể được
gọi là đối tượng và sau đó xây dựng các dữ liệu cùng các hàm xung quanh các đối tượng đó.
Các đối tựng có thể tác động, trao đổi thông tin với nhau thông qua cơ chế thông
báo(mesage) thông qua các phương thưc(hàm). Đối tượng A Đối tượng B Dữ liệu Dữ liệu Các phương thức Các phương thức Đối tượng C Dữ liệu Các phương thức
Lập trình hướng đối tượng có các đặc tính sau:
Đề cương Lập trình hướng đối tượng với C# Trang 9
+ Tập chung vào dữ liệu thay cho các hàm
+ Chương trình được chia thành các đối tượng
+ Cấu trúc dữ liệu được thiết kế sao cho đặc tả được đối tượng gắn với cấu trúc dữ liệu đó.
+ Dữ liệu được đóng gói lại, được che giấu và không cho phép các hàm ngoại lai truy nhập tự do.
+ Các đối tượng tác động và trao đổi thông tin với nhau qua các hàm.
+ Có thể dẽ dàng bổ sung dữ liệu và các hàm mới vào đối tượng nào đó khi cần thiết.
+ Chương trình được thiết kế theo cách tiếp cận từ dưới lên(bottom- up).
1.2 Một số khái niệm trong lập trình hướng đối tượng
1.2.1 Trừu tượng hóa dữ liệu (Data Abstraction)
Khi một lập trình viên phải phát triển một chương trình ứng dụng thì không có
nghĩa là người ấy lập tức viết mã cho ứng dụng ấy. Trước hết, người ấy phải nghiên cứu
ứng dụng và xác định những thành phần tạo nên ứng dụng. Kế tiếp, người ấy phải xác
định những thông tin cần thiết về mỗi thành phần.
Hãy khảo sát chương trình ứng dụng cho việc mua bán xe hơi nói trên. Chương trình phải
xuất hóa đơn cho những xe hơi đã bán cho khách hàng. Để xuất một hóa đơn, chúng ta
cần những thông tin chi tiết về khách hàng. Vậy bước thứ nhất là xác định những đặc tính của khách hàng.
Một vài đặc tính gắn kết với khách hàng là: ➢ Tên. ➢ Địa chỉ. ➢ Tuổi. ➢ Chiều cao. ➢ Màu tóc.
Từ danh sách kể trên, chúng ta xác định những đặc tính thiết yếu đối với ứng dụng. Bởi
vì chúng ta đang đề cập đến những khách hàng mua xe, vì thế những chi tiết thiết yếu là:
Đề cương Lập trình hướng đối tượng với C# Trang 10 ➢ Tên. ➢ Địa chỉ.
Còn những chi tiết khác (chiều cao, màu tóc …) là không quan trọng đối với ứng dụng.
Tuy nhiên, nếu chúng ta phát triển một ứng dụng hỗ trợ cho việc điều tra tội phạm thì
những thông tin chẳng hạn như màu tóc là thiết yếu.
Bên cạnh những chi tiết về khách hàng, những thông tin sau cũng cần thiết: ➢ Kiểu xe được bán. ➢ Nhân viên nào bán xe.
Bên cạnh những đặc tính của khách hàng, xe hơi và nhân viên bán hàng, chúng ta cũng
cần liệt kê những hành động được thực hiện.
Công việc xuất hóa đơn đòi hỏi những hành động sau:
➢ Nhập tên của khách hàng.
➢ Nhập địa chỉ của khách hàng. ➢ Nhập kiểu xe.
➢ Nhập tên của nhân viên bán xe.
➢ Xuất hóa đơn với định dạng đòi hỏi.
Khung thông tin bên dưới cho thấy những thuộc tính và những hành động liên quan đến một hóa đơn: Các thuộc tính Tên của khách hàng
Địa chỉ của khách hàng Kiểu xe bán Nhân viên bán xe Các hành động Nhập tên
Đề cương Lập trình hướng đối tượng với C# Trang 11 Nhập địa chỉ Nhập kiểu xe
Nhập tên nhân viên bán xe Xuất hóa đơn Định nghĩa
Sự trừu tượng hóa dữ liệu là quá trình xác định và nhóm các thuộc tính và các hành
động liên quan đến một thực thể cụ thể, xét trong mối tương quan với ứng dụng đang phát triển.
Tiếp theo, chúng ta muốn ứng dụng tính toán tiền hoa hồng cho nhân viên bán hàng.
Những thuộc tính liên kết với nhân viên bán hàng có tương quan với ứng dụng này là: ➢ Tên.
➢ Số lượng xe bán được. ➢ Tiền hoa hồng.
Những hành động đòi buộc đối với công việc này là:
➢ Nhập tên nhân viên bán xe.
➢ Nhập số lượng xe bán được.
➢ Tính tiền hoa hồng kiếm được. Những thuộc tính Tên
Số lượng xe bán được Tiền hoa hồng Những hành động Nhập tên
Đề cương Lập trình hướng đối tượng với C# Trang 12
Nhập số lượng xe bán được Tính tiền hoa hồng
Như thế, việc trừu tượng hóa dữ liệu tra đặt ra câu hỏi ‘Đâu là những thuộc tính và những
hành động cần thiết cho một vấn đề đặt ra?’
Những ưu điểm của việc Trừu tượng hóa:
➢ Tập trung vào vấn đề.
➢ Xác định những đặc tính thiết yếu và những hành động cần thiết.
➢ Giảm thiểu những chi tiết không cần thiết.
Việc trừu tượng hóa dữ liệu là cần thiết, bởi vì không thể mô phỏng tất cả các hành động
và các thuộc tính của một thực thể. Vấn đề mấu chốt là tập trung đến những hành vi cốt
yếu và áp dụng chúng trong ứng dụng.
Chẳng hạn như khách hàng hoặc nhân viên bán hàng cũng có thể thực hiện những hành động sau: ➢ Người ấy đi lại.
➢ Người ấy nói chuyện.
Tuy nhiên, những hành động này không liên quan đến ứng dụng. Việc trừu tượng hóa dữ
liệu sẽ loại bỏ chúng.
1.2.2 Lớp (Class)
Trong ứng dụng mua bán xe, chúng ta đã xác định các thuộc tính và các hành
động cần có để xuất một hóa đơn cho một khách hàng.
Các hành động và các thuộc tính này là chung cho mọi khách hàng mua xe. Ví thể, chúng
có thể được nhóm lại trong một thực thể đơn nhất gọi là một ‘lớp’.
Hãy khảo sát lớp có tên là ‘khách hàng’ dưới đây. Lớp này bao gồm mọi thuộc tính và
hành động đòi hỏi đối với một khách hàng. Lớp Khách hàng Tên khách hàng
Đề cương Lập trình hướng đối tượng với C# Trang 13 Địa chỉ khách hàng Kiểu xe được bán Nhân viên bán xe Nhập tên Nhập địa chỉ
Nhập kiểu xe được bán
Nhập tên nhân viên bán xe Xuất hóa đơn Định nghĩa
Một lớp định nghĩa một thực thể theo những thuộc tính và những hành động chung. Hoặc
Những thuộc tính và những hành động chung của một thực thể được nhóm lại để tạo nên
một đơn vị duy nhất gọi là một lớp. Hoặc Một lớp là một sự xác định cấp chủng loại của
các thực thể giống nhau.
Một lớp là một mô hình khái niệm về một thực thể. Nó mang tính cách tổng quát chứ
không mang tính cách đặc thù.
Khi định nghĩa một lớp, chúng ta muốn phát biểu rằng một lớp sẽ phải có một tập hợp
các thuộc tính và các hành động riêng. Chẳng hạn như một định nghĩa lớp dưới đây: Lớp Con người Tên Chiều cao Màu tóc Viết Nói
Lớp này định nghĩa thực thể ‘Con người’. Mọi thực thể thuộc kiểu ‘Con người’ sẽ đều có
những đặc tính và những hành động như đã được định nghĩa.
Đề cương Lập trình hướng đối tượng với C# Trang 14
Một khi một lớp đã được định nghĩa, chúng ta biết được những thuộc tính và những hành
động của những thực thể ‘trông giống’ như lớp này. Vì thế, tự bản chất một lớp là một nguyên mẫu (prototype).
Một ví dụ khác về một lớp liên quan đến việc mua bán xe hơi như sau:
Lớp Nhân viên bán hàng Tên
Số lượng xe bán được Tiền hoa hồng Nhập tên
Nhập số lượng xe bán được Tính tiền hoa hồng
Lớp trên định nghĩa các thuộc tính và các hành động đặc trưng cho mọi nhân viên bán xe hơi.
1.2.3 Đối tượng (Object)
Một lớp là một nguyên mẫu phác họa những thuộc tính và những hành động có thể
của một thực thể. Để có thể sử dụng thực thể mà lớp định nghĩa, chúng ta phải tạo một
‘đối tượng’ từ lớp đó.
Lớp là một khái niệm, còn đối tượng là một thể hiện được định nghĩa bởi lớp.
Hãy khảo sát lớp ‘Khách hàng’ được định nghĩa trên. Lớp này định nghĩa mọi thuộc
tính và hành động gắn liền với một khách hàng.
Khi một người mua một xe hơi ở một cửa hàng, cửa hàng ấy có một khách hàng mới.
Vào thời điểm ấy, một đối tượng giống như lớp ‘Khách hàng’ được tạo ra. Đối tượng này
sẽ phải có những giá trị thực đối với các thuộc tính ‘Tên’, ‘Địa chỉ’, ‘Kiểu xe’ …
Chẳng hạn như một khách hàng có tên là ‘Mark’, sống ở ‘London’ đã mua một xe
kiểu ‘Honda Civic’ từ nhân viên bán hàng tên là ‘Tom’. Như thế, ‘Mark’ là một đối
tượng của kiểu ‘Khách hàng’.
Đề cương Lập trình hướng đối tượng với C# Trang 15
Định nghĩa: Đối tượng là sự kết hợp giữa dữ liệu và thủ tục ( hay còn gọi là các
phương thức – method) thao tác trên dữ liệu đó. Có thể đưa ra công thức phản ánh bản
chất kỹ thuật lập trình hướng đối tượng như sau:
Đối tượng=Dữ liệu + Phương thức
Một đối tượng là một thực thể cụ thể (thông thường bạn có thể sờ chạm, xem thấy và cảm nhận).
Kể từ lúc một đối tượng hiện hữu, những thuộc tính của nó là những giá trị xác định, và
những hành động được định nghĩa cho đối tượng này được thực thi.
Trong mỗi một đối tượng, các khía cạnh sau đây được xác định rõ: ➢ Tình trạng (state). ➢ Thái độ (behavior). ➢ Chân tính (identity).
Hình: Trình bày hai đối tượng.
Mỗi đối tượng có những đặc tính riêng mô tả đối tượng ấy là gì, hoặc hành động ra sao.
Chẳng hạn như những thuộc tính của một đối tượng ‘Con người’ sẽ là: ➢ Tên. ➢ Tuổi. ➢ Trọng lượng.
Những thuộc tính của một đối tượng ‘Xe hơi’ sẽ là: ➢ Màu sắc.
Đề cương Lập trình hướng đối tượng với C# Trang 16 ➢ Kiểu xe. ➢ Năm.
Một đối tượng cũng thực hiện một số hành động. Một xe hơi có khả năng thực hiện những hành động sau: ➢ Khởi động. ➢ Ngưng. ➢ Chuyển động.
Để chuyển đổi giữa các đối tượng lập trình và các đối tượng đời thực, cần phải kết hợp
các thuộc tính và các hành động của một đối tượng.
1.2.4 Thuộc tính
Chúng ta xác định các thuộc tính và các hành động để định nghĩa một lớp. Một khi các
thuộc tính được gán cho các giá trị, chúng mô tả một đối tượng. Hãy khảo sát lớp sau:
Các thuộc tính của lớp Khách hàng Tên của khách hàng
Địa chỉ của khách hàng Kiểu xe được bán Nhân viên đã bán xe
Khi thuộc tính ‘Tên’ được gán cho giá trị ‘Mark’ thì nó mô tả một đối tượng xác định
được tạo từ lớp ‘Khách hàng’. Định nghĩa
Một thuộc tính là một đặc tính mô tả một đối tượng.
Như thế, các thuộc tính nắm giữ các giá trị dữ liệu trong một đối tượng, chúng định nghĩa
một đối tượng cụ thể.
Đề cương Lập trình hướng đối tượng với C# Trang 17
Bởi vì một lớp là một nguyên mẫu cho nên các thuộc tính trong một lớp không thể nắm
giữ các giá trị. Một thuộc tính có thể được gán một giá trị chỉ sau khi một đối tượng dựa
trên lớp ấy được tạo ra.
Để có thể lưu giữ những chi tiết của một khách hàng, một thể hiện(đối tượng) của lớp
‘Khách hàng’ phải được tạo ra. Các thuộc tính của một đối tượng hiện hữu chỉ khi đối
tượng ấy được tạo ra.
Mọi đối tượng của một lớp phải có cùng các thuộc tính. Khảo sát ví dụ sau:
Các thuộc tính của lớp Con người
Đối tượng được tạo từ lớp Con người Tên Mark = Chiều cao 6 ft. 1 in. Màu tóc Black
1.2.5 Hoạt động (Operation)
Các hành động khả thi, như được định nghĩa trong một lớp, được gọi là ‘các hoạt động’. Định nghĩa:
Một hoạt động là một dịch vụ được yêu cầu của một đối tượng.
Các hoạt động xác định các hành động cần phảit thực hiện của một đối tượng được tạo ra
từ một lớp. Chẳng hạn như chúng ta không thể yêu cầu một hoạt động ‘Mua một xe hơi
khác’ của một đối tượng được tạo ra từ lớp ‘Khách hàng’.
Một lớp chỉ là một nguyên mẫu. Vì thế, trong một lớp một hoạt động chỉ được định
nghĩa. Còn việc áp dụng hoạt động ấy chỉ xảy ra nơi các đối tượng riêng rẽ. Chẳng hạn
như hoạt động ‘Nhập Tên’ mà lớp “Khách hàng’ định nghĩa có thể được thực hiện nơi
một đối tượng nào đó.
Tập hợp các hoạt động được yêu cầu cho tất cả các đối tượng trong cùng một lớp là như nhau.
1.2.6 Phương thức (Method)
Các hoạt động định nghĩa các hành động khả thi có thể được yêu cầu của một đối tượng.
Một phương thức là sự thực thi thực tế của một hoạt động.
Đề cương Lập trình hướng đối tượng với C# Trang 18 Định nghĩa
Phương thức là sự xác định về cách thức thực thi một hoạt động được yêu cầu.
Các phương thức xác định cách thức thao tác trên các dữ liệu của một đối tượng. Bởi vì
phương thức là sự thực thi thực tế một hoạt động, cho nên nó có thể được áp dụng cho
một đối tượng. Một phương thức là một thuật toán, từng bước từng bước (step-by-step)
xác định điều gì được thực hiện khi hoạt động ấy được yêu cầu.
Hãy khảo sát những hoạt động chung của một thực thể thuộc loại ‘Con người’: Đi, Nói.
Chỉ khi một đối tượng cụ thể của loại ‘Con người’ được tạo ra thì các hành động ‘Đi’,
‘Nói’ mới được thực thi.
1.2.7 Thông điệp (Message)
Để yêu cầu một hoạt động cụ thể nào đó được thực hiện, một thông điệp được gởi tới đối
tượng nơi hoạt động này được định nghĩa. Định nghĩa
Một thông điệp là một lời yêu cầu một hoạt động.
Khi một đối tượng nhận được một thông điệp, nó thực hiện một phương thức tương ứng.
Chẳng hạn, một đối tượng được tạo từ lớp ‘Khách hàng’ để nhập tên của người sử dụng.
Khi đối tượng nhận được thông điệp, nó tìm và thực thi phương thức ‘Nhập tên’.
Trong trường hợp một công ty, mỗi bộ phận được coi là một đối tượng. Những thông tin
được chuyển tới và được đón nhận từ mỗi bộ phận (hoặc qua thông báo liên bộ phận,
hoặc qua những chỉ thị miệng) tạo nên những thông điệp giữa các đối tượng. Những chỉ
thị này có thể được chuyển dịch thành những lời gọi hàm trong một chương trình.
Các đối tượng gởi thông điệp cho nhau
Đề cương Lập trình hướng đối tượng với C# Trang 19
Trong hình trên ‘Kinh doanh’ và ‘Kế toán’ là hai bộ phận khác nhau trong một công ty.
Hai bộ phận này được coi là hai đối tượng khác nhau. Thông tin được truyền đi và được
đón nhận giữa các bộ phận tạo nên các thông điệp giữa các đối tượng.
1.2.8 Sự kiện (Event)
Một sự kiện là một sự việc xảy ra cho một đối tượng tại một thời điểm. Để đáp ứng lại sự
kiện ấy, đối tượng sẽ thực hiện một hoặc nhiều phương thức.
Nói cách khác, một sự kiện là một tác nhân mà đối tượng này gây ra cho một đối tượng
khác. Chẳng hạn như click chuột trái trên một nút.
Để hiểu rõ hơn các sự kiện, hãy khảo sát ví dụ sau từ thực tế:
‘Một người sẽ thét lên khi bị thọc bằng một vật nhọn’.
‘Thọc’ là sự kiện gây ra sự phản ứng là ‘thét lên’.
Trong máy tính, một người sử dụng nhấn một nút trên bàn phím là một sự kiện chung. Sự
phản hồi đối với sự kiện này là việc hiển thị ký tự tương ứng trên màn hình.
1.2.8 Phân biệt giữa lớp và đối tượng
Có một sự khác biệt thực sự giữa một lớp và một đối tượng. Cần nhận thức rõ sự khác biệt này.
Một lớp định nghĩa một thực thể, trong khi đó một đối tượng là một trường hợp của thực thể ấy.
Đối tượng là một mô hình thực, trong khi lớp là một mô hình khái niệm - định nghĩa tất
cả các thuộc tính và các phương thức cần thiết của một đối tượng.
Tất cả các đối tượng thuộc về cùng một lớp có cùng các thuộc tính và các phương thức.
Một lớp là một nguyên mẫu của một đối tượng. Nó xác định các hành động khả thi và các
thuộc tính cần thiết cho một nhóm các đối tượng cụ thể.
1.2.9 Thiết lập (Construction) và Hủy (Destruction) 1.2.9.1 Thiết lập
Một lớp chỉ cung cấp những định nghĩa về các thuộc tính và các phương thức khả
thi. Các thuộc tính và các phương thức có thể được truy cập chỉ khi một đối tượng dựa
trên một lớp được tạo ra.
Đề cương Lập trình hướng đối tượng với C# Trang 20
Khi một đối tượng mới được tạo, các thuộc tính của nó trở nên hiện thực và có thể được
gán giá trị. Tương tự, các phương thức đã được định nghĩa cũng được áp dụng. Định nghĩa
Thiết lập là một tiến trình hiện thực hóa một đối tượng.
Hàm thiết lập là một phương thức đặc biệt phải được gọi trước khi sử dụng bất kỳ
phương thức nào trong một lớp. Hàm Thiết lập khởi tạo các thuộc tính, và cấp phát bộ nhớ nếu cần.
Mỗi một lớp có một hàm thiết lập.
Khảo sát lại trường hợp cửa hàng bán xe hơi. Ngay từ lúc đầu chỉ định nghĩa các lớp.
Cho đến khi một khách hàng mua một xe hơi tại cửa hàng thì một đối tượng mới giống
như lớp ‘Khách hàng’ mới được tạo.
Khi đối tượng này được tạo, một số khoảng trống bộ nhớ được cấp phát cho những thuộc
tính của nó để lưu trữ các giá trị được gán cho các thuộc tính ấy (‘Tên’, ‘Địa chỉ’ …).
Hàm thiết lập thực hiện việc cấp phát này. Vào lúc này, mọi thuộc tính và phương thức
của đối tượng sẵn sàng để sử dụng.
Tương tự như trường hợp một học sinh nhập học tại một trường học. Khi một học sinh
nhập học, một vài hành động được thực hiện để nhận học sinh ấy vào trường. Đó là:
➢ Xếp lớp cho học sinh ấy.
➢ Ghi tên học sinh ấy vào danh sách. ➢ Xếp chỗ ngồi.
Đây là những hành động đồng loạt được thực hiện ngay lúc bắt đầu nhập học. Chúng
tương tự với những hành động mà hàm thiết lập của một đối tượng thực hiện. 1.2.9.2 Hủy
Khi một đối tượng không còn cần thiết nữa thì nó sẽ bị hủy bỏ. Sẽ lãng phí tài nguyên,
chẳng hạn như bộ nhớ, nếu như tiếp tục để cho một đối tượng tồn tại một khi nó không còn cần thiết.
Đề cương Lập trình hướng đối tượng với C# Trang 21 Định nghĩa
Hàm Hủy là một phương thức đặc biệt được dùng để hủy bỏ một đối tượng. Tiến trình
Hủy tiêu hủy một đối tượng và giải phóng khoảng trống bộ nhớ mà hàm thiết lập đã cấp
phát cho nó. Hàm Hủy cũng triệt tiêu khả năng truy cập đến đối tượng ấy.
Một khi một đối tượng bị hủy thì các thuộc tính của nó không thể được truy cập, cũng
như không một phương thức nào có thể được thực thi.
Chẳng hạn, trong trường hợp bán xe hơi, một khi nhân viên bán hàng bỏ nghề, những chi
tiết của người ấy không còn liên hệ. Vì thế, đối tượng tương ứng sẽ bị hủy. Điều này giải
phóng bộ nhớ đã cấp phát cho nhân viên bán hàng ấy. Khoảng trống này giờ đây có thể được tái sử dụng.
Hãy xem xét ví dụ về trường học trên đây. Khi một học sinh thôi học, tên của học sinh ấy
bị loại ra khỏi danh sách, và khoảng trống được giải phóng có thể được tái cấp phát.
Các hành động đồng loạt này tương tự với công việc của hàm hủy đối với một đối tượng.
1.2.10 Tính Bền vững (Persistence)
Hãy khảo sát trường hợp bán xe hơi. Những chi tiết của khách hàng được lưu trữ ngay
khi xe hơi đã được phân phối.Việc duy trì dữ liệu vẫn cần thiết cho đến khi dữ liệu được
chỉnh sửa hoặc hủy bỏ chính thức. Định nghĩa
Tính Bền vững là khả năng lưu trữ dữ liệu của một đối tượng ngay cả khi đối tượng ấy không còn tồn tại.
Cửa hàng bán xe lưu trữ chi tiết khách hàng vào một file. Những chi tiết này sẽ tồn tại
trong file cho đến khi chúng bị hủy, hoặc bản thân file bị hủy.
Chúng ta đụng chạm tính bền vững mỗi ngày. Hãy xem việc sáng tác một bài thơ. Bài thơ
là dữ liệu tồn tại trong tâm trí của nhà thơ. Bao lâu nhà thơ còn tồn tại thì bấy lâu bài thơ
còn tồn tại. Nếu bài thơ muốn tồn tại ngay cả sau khi nhà thơ qua đời thì nó phải được viết ra giấy.
Bài thơ được viết ra giấy tạo nên sự bền vững. Bài thơ sẽ tồn tại bao lâu văn bản ấy còn
được duy trì. Bài thơ ấy không còn tồn tại khi tờ giấy ấy bị xé rách, hoặc chữ nghĩa bị xóa đi.
Đề cương Lập trình hướng đối tượng với C# Trang 22
1.2.11 Tính Đóng gói dữ liệu
Tiến trình trừu tượng hóa dữ liệu hỗ trợ cho việc xác định những thuộc tính và những
phương thức thiết yếu.
Thông thường, các đối tượng sử dụng những thuộc tính và những phương thức không
được yêu cầu bởi người sử dụng đối tượng.
Chẳng hạn như trong trường hợp lớp ‘Khách hàng’. Lớp ấy có một phương thức xuất hóa
đơn. Giả sử rằng khi hóa đơn được xuất, một trong những chi tiết được in ra trên hóa đơn
là ngày phân phối. Tuy nhiên chúng ta không biết thuộc tính nào qua đó chúng ta có thể xác định thông tin này.
Ngày phân phối được phát sinh bên trong đối tượng, và được hiển thị trên hóa đơn. Như
thế người sử dụng không nhận thức về cách thức mà ngày phân phối được hiển thị.
Ngày phân phối có thể được xử lý theo một trong những cách sau:
➢ Đó là một giá trị được tính toán - Chẳng hạn, 15 ngày kể từ ngày đặt hàng.
➢ Đó là một giá trị cố định – Xe hơi được phân phối vào ngày mùng 2 mỗi tháng.
Đối tượng sử dụng những thuộc tính và những phương thức mang tính nội bộ. Bởi vì
những thuộc tính và những phương thức có thể được che khuất khỏi tầm nhìn. Các đối
tượng khác và những người sử dụng không nhận thức được các thuộc tính và / hoặc các
phương thức như thế có tồn tại hay không. Tiến trình che giấu các thuộc tính, các phương
thức, hoặc các chi tiết của việc thi hành được gọi là ‘đóng gói’ (encapsulation). Định nghĩa
Đóng gói là tiến trình che giấu việc thực thi những chi tiết của một đối tượng đối với
người sử dụng đối tượng ấy.
Việc đóng gói phân tích những khía cạnh có thể truy cập từ bên ngoài với những khía
cạnh chỉ được sử dụng trong nội bộ của đối tượng.
Ưu điểm của việc đóng gói là có thể tạo ra bất kỳ thuộc tính hay phương thức cần thiết để
đáp ứng đòi hỏi công việc khi xây dựng một lớp. Mặt khác, chỉ những thuộc tính và /
hoặc những phương thức có thể được truy cập từ bên ngoài lớp thì mới nhìn thấy.
Đề cương Lập trình hướng đối tượng với C# Trang 23
Một ví dụ khác về việc đóng gói là lớp ‘Nhân viên bán hàng’ đã được định nghĩa ở trên.
Khi phương thức tính tiền hoa hồng được thực thi, người sử dụng không biết chi tiết của
việc tính toán. Tất cả những gì họ biết chỉ là tổng số tiền hoa hồng mà họ phải trả cho nhân viên bán hàng.
Một trường hợp về đóng gói mà chúng ta gặp trong đời sống hằng ngày là việc giao dịch
kinh doanh ở một cửa hàng. Khách hàng yêu cầu sản phẩm X. Họ được trao cho sản
phẩm X, và họ phải trả tiền cho sản phẩm ấy. Sau khi khách hàng yêu cầu sản phẩm,
người bán hàng thực hiện những hành động sau:
➢ Kiểm tra mặt hàng trên kệ hàng.
➢ Giảm số lượng mặt hàng trong bảng kiểm kê sau khi bán.
Tuy nhiên, khách hàng không được biết những chi tiết này.
1.2.12 Tính thừa kế
Hãy khảo sát các lớp sau: Lớp Sinh viên Lớp Nhân viên Lớp Khách hàng Tên Tên Tên Địa chỉ Địa chỉ Địa chỉ Điểm môn 1 Lương Kiểu xe đã bán Điểm môn 2 Chức vụ Nhập tên Nhập tên Nhập tên Nhập địa chỉ Nhập địa chỉ Nhập địa chỉ Nhập kiểu xe Nhập điểm Nhập chức vụ Xuất hóa đơn Tính tổng điểm Tính lương
Trong tất cả ba lớp, chúng ta thấy có một vài thuộc tính và hoạt động chung. Chúng ta
muốn nhóm những thuộc tính và những hoạt động ấy lại, và định nghĩa chúng trong một lớp ‘Người’.
Đề cương Lập trình hướng đối tượng với C# Trang 24 Lớp Người Tên Địa chỉ Nhập tên Nhập địa chỉ
Ba lớp ‘Sinh viên’, ‘Nhân viên’ và ‘Khách hàng’ có những thành phần giống lớp
‘Người’. Nói cách khác, ba lớp ấy có tất cả các thuộc tính và các phương thức của lớp
‘Người’, ngoài ra chúng còn có những thuộc tính và những phương thức riêng.
Chúng ta cần phải định nghĩa lớp ‘Người’ và sử dụng nó trong khi định nghĩa các lớp
‘Sinh viên’, ‘Nhân viên’ và ‘Khách hàng’.
Chúng ta xây dựng một lớp ‘Người’ với những thuộc tính và những hoạt động như đã
trình bày ở hình trên. Kế tiếp, chúng ta xây dựng lớp ‘Khách hàng’ bao gồm lớp ‘Người’
cộng với những thuộc tính và những phương thức riêng.
Chúng ta có thể định nghĩa các lớp ‘Sinh viên’ và ‘Nhân viên’ theo cùng cách thức trên.
Như thế, cả ba lớp ‘Khách hàng’, ‘Sinh viên’ và ‘Nhân viên’ đều chia sẻ những thuộc
tính và những phương thức mà lớp ‘Người’ cung cấp. Lớp Sinh viên Lớp Nhân viên Lớp Khách hàng Điểm môn 1 Lương Kiểu xe bán được Điểm môn 2 Chức vụ Nhập kiểu xe Nhập điểm Nhập chức vụ Xuất hóa đơn tính tổng điểm Tính lương
Theo ngôn ngữ hướng đối tượng, lớp ‘Khách hàng’ được gọi là thừa kế lớp ‘Người’.
Định nghĩa: Tính thừa kế cho phép một lớp chia sẻ các thuộc tính và các phương thức
được định nghĩa trong một hoặc nhiều lớp khác.
Có hai khái niệm quan trọng khác liên kết với tính thừa kế. Lớp ‘Khách hàng’ là lớp
‘Người’ cộng thêm cái khác. Như thế, lớp ‘Khách hàng’ có tất cả các thuộc tính và các
phương thức được định nghĩa trong lớp ‘Người’ cộng với các thuộc tính và các hoạt động của riêng nó.
Đề cương Lập trình hướng đối tượng với C# Trang 25
Trong ví dụ này, lớp ‘Khách hàng’ được gọi là ‘lớp con’ (subclass).
Định nghĩa: Lớp thừa hưởng từ một lớp khác được gọi là Subclass.
Trong ví dụ trên, lớp ‘Người’ được coi là ‘lớp trên’ (superclass).
Định nghĩa: Một Superclass là một lớp mà các đặc tính của nó được một lớp khác thừa hưởng.
Hãy xem xét ví dụ về lớp ‘Các động vật’ ở hình dưới đây ‘Các động vật’ là lớp trên cùng
mà các lớp khác kế thừa. Chúng ta có một dãy các lớp trung gian – ‘Côn trùng’, ‘Hữu
nhũ’, ‘Bò sát’, ‘Lưỡng cư’ - mà dãy các lớp dưới kế thừa.
Các lớp ‘Côn trùng’, ‘Hữu nhũ’, ‘Bò sát’, ‘Lưỡng cư’ là những lớp con của lớp trên ‘Các
động vật’. Như thế, những lớp này có tất cả những thuộc tính và các hoạt động của lớp
‘Các động vật’, cộng thêm những thuộc tính và những phương thức của riêng chúng.
Lớp ‘Hữu nhũ’ là lớp mà các lớp ‘Con người’ và ‘Khác con người’ thừa kế. Như thế, các
lớp ‘Con người’ và ‘Khác con người’ là các lớp con của lớp trên ‘Hữu nhũ’. Côn trùng Hữu nhũ Bò sát Lưỡng cư Con người Khác con người
1.2.13 Tính Đa Thừa kế
Trong tất cả các ví dụ trên, một lớp thừa kế chỉ từ một lớp. Ngay cả trong ví dụ thừa kế
về các loại phương tiện di chuyển, mỗi lớp con chỉ có một lớp cha. Trường hợp như thế
gọi là ‘thừa kế đơn’ (single inheritance).
Trong ‘đa thừa kế’, một lớp con thừa kế từ hai hay nhiều lớp cha.
Đề cương Lập trình hướng đối tượng với C# Trang 26
Hãy khảo sát ví dụ sau:
Lớp Đường thẳng Lớp Đường tròn Lớp Hình ảnh Lớp Vẽ một hình Khởi điểm Bán kính Hình ảnh Điểm tận cùng Tâm điểm Vẽ hình ảnh Nhận hình vẽ Vẽ đường thẳng + Vẽ đường tròn + = Vẽ hình
Trong hình trên, chúng ta đã xây dụng một lớp ‘Vẽ một hình’, lớp này thừa hưởng ba
lớp: ‘Đường thẳng’, ‘Đường tròn’, ‘Hình ảnh’. Như thế lớp ‘Vẽ một hình’ kết hợp chức
năng của ba lớp trên thêm vào chức năng được định nghĩa bên trong nó.
Lớp ‘Vẽ một hình’ là một ví dụ về tính đa thừa kế.
Có thể sử dụng tính đa thừa kế để xây dựng một lớp mới, lớp này dẫn xuất chức năng của
nó từ một vài lớp khác. Như thế, xét theo góc cạnh của người sử dụng lớp mới này, chỉ
cần một lớp mà cung cấp tất cả các chức năng. Như vậy, họ không cần phải sử dụng
nhiều đối tượng khác nhau.
Sự thuận lợi quan trọng nhất của tính thừa kế là nó thúc đẩy việc tái sử dụng mã chương trình.
Trong ví dụ trên, chúng ta có ba lớp ‘Đường thẳng’, ‘Đường tròn’ và ‘Hình ảnh’. Giả
thiết rằng ba người khác nhau xây dựng ba lớp này riêng biệt. Bây giờ, người sử dụng
cần xây dựng một lớp để vẽ đường thẳng, vẽ đường tròn cũng như hiển thị hình ảnh. Vì
thế họ tìm kiếm xem có lớp nào đáp ứng một hoặc tất cả các yêu cầu đó. Nếu có những
lớp cung cấp chức năng thỏa yêu cầu thì người sử dụng sẽ thừa kế những lớp đó để tạo một lớp mới.
Giờ đây người sử dụng chỉ còn phải viết mã chương trình cho những đặc tính chưa có sau
tiến trình thừa kế. Người sử dụng có thể sử dụng chính ba lớp trên. Tuy nhiên, sự thừa kế
cung cấp một bó những chức năng hỗn độn trong một lớp.
1.2.14 Tính Đa hình
Trong một chương trình có cấu trúc (a structured program), một phương thức chỉ ứng
dụng cho một đối tượng. Chẳng hạn xét toán tử ‘Cộng’. Toán tử này chỉ tính tổng của hai
số nguyên. Khi truyền hai giá trị 2 và 3 thì nó hiển thị 5. Chúng ta không thể có một loại
toán tử ‘Cộng’ để tính tổng của hai giá trị văn bản (text) ‘Hello!’ và ‘How are you?’ để
có được chuỗi văn bản kết quả ‘Hello! How are you?’
Đề cương Lập trình hướng đối tượng với C# Trang 27
Trong hệ thống hướng đối tượng thì tình huống mô tả trên là có thể. Định nghĩa
Tính đa hình cho phép một phương thức có các cách thể hiện khác nhau trên nhiều loại đối tượng khác nhau.
Với tính đa hình, nếu cùng một phương thức ứng dụng cho các đối tượng thuộc các lớp
khác nhau thì nó đưa đến những kết quả khác nhau. Bản chất của sự việc chính là phương
thức này bao gồm cùng một số lượng các tham số.
Tính đa hình là một trong những đặc tính quan trọng nhất của hệ thống hướng đối tượng.
Một ví dụ khác là phương thức hiển thị. Tùy thuộc vào đối tượng tác động, phương thức
ấy có thể hiển thị một chuỗi, hoặc vẽ một đường thẳng, hoặc hiển thị một hình ảnh. Hãy khảo sát hình sau: Lớp: Hình thể Các lớp con Các phương thức: Vẽ
Lớp ‘Hình thể’ và các lớp con
Hình trên cho thấy rằng ‘Vẽ’ là một phương thức được chia sẻ giữa các lớp con của lớp
‘Hình thể’. Tuy nhiên, phương thức Vẽ được ứng dụng cho hình hộp sẽ khác với hình êlip.
Tính đa hình hỗ trợ tính đóng gói.
Xét trên mức độ người sử dụng, họ chỉ cần một phương thức ‘Vẽ’ của lớp ‘Hình thể’.
Còn cách thức mà phương thức ‘Vẽ’ được thực thi cho các trường hợp khác nhau thì họ không cần biết.
Đề cương Lập trình hướng đối tượng với C# Trang 28
1.3 Các ưu điểm của lập trình hướng đối tượng
1.3.1. Các ưu điểm của lập trình hướng đối tượng
LTHĐT đem lại một số lợi thế cho người thiết kế lẫn người lập trình. Cách tiếp
cận hướng đối tượng giải quyết được nhiều vấn đề tồn tại trong quá trình phát triển phần
mềm và tạo ra được những phần mềm có độ phức tạp và chất lượng cao. Phương pháp
này mở ra triển vọng to lớn cho người lập trình. Nhưng ưu điểm chính của LTHĐT là:
1. Thông qua nguyên lý kế thừa, chúng ta có thể loại bỏ được những đoạn chương
trình lặp lại trong quá trình mô tả các lớp và có thể mở rộng khả năng sử dụng của các
lớp đã xây dựng mà không cần phải viết lại.
2. Chương trình được xây dựng từ những đơn thể( đối tượng) trao đổi với nhau
nên việc thiết kế và lập trình sẽ được thực hiện theo quy trình nhất định chứ không phải
dựa vào kinh nghiệm và kỹ thuật như trước nữa. Điều này đảm bảo rút ngắn được thời
gian xây dựng hệ thống và tăng năng xuất lao động.
3. Nguyên lý đóng gói hay che dấu thông tin giúp người lập trình tạo ra được
những chương trình an toàn không bị thay đổi bởi những đoạn chương trình khác.
4. Có thể xây dựng được ánh xạ các đối tượng của bài toán vào đối tượng chương trình.
5. Cách tiếp cận thiết kế đặt trọng tâm vào dữ kiệu, giúp chúng ta xây dựng được
mô hình chi tiết và dẽ dàng cài đặt hơn.
6. Các hệ thống hướng đối tượng dẽ mở rộng, nâng cấp thành những hệ lớn
7. Kỹ thuật truyền thông báo trong việc trao đổi thông tin giữa các đối tượng làm
cho việc mô tả giao diện với các hệ thống bên ngoài trở nên đơn giản hơn,
8. Có thể quản lý được độ phức tạp của sản phẩm phần mềm.
1.3.2. Mốt số ngôn ngữ lập trình hướng đối tượng
Lập trình hướng đối tượng không phải là đặc quyền của một ngôn ngữ đặc biệt
nào. Cũng giống như kỹ thuật lập trình có cấu trúc, các khái niệm trong lập trình hướng
đối tượng được thể hiện trong nhiều ngôn ngữ lập trình khác nhau. Những ngôn ngữ cung
cấp được những khả năng lập trình hướng đối tượng được gọi là ngôn ngữ lập trình
hướng đối tượng. Tuy vẫn có những ngôn ngữ chỉ cung cấp khả năng tạo lớp và đối
Đề cương Lập trình hướng đối tượng với C# Trang 29
tượng mà không cho phép kế thừa, do đó hạn chế khả năng lập trình hướng đối tượng.
Hình dưới đây cho ta một cái nhìn tổng quan về sự phát triển các ngôn ngữ lập trình hướng đối tượng.
Đây là hình ảnh sự phát triển các ngôn ngữ lập trình hướng đối tượng
Các ngôn ngữ SIMULA, SMALLTALK, JAVA, C# thuộc họ ngôn ngữ lập trình
hướng đối tượng thuần khiết, nghĩa là nó không cho phép phát triển các chương trình cấu
trúc trên các ngôn ngữ loại này.
Lập trình hướng đối tượng là một trong những thuật ngữ được nhắc đến nhiều nhất
hiện nay trong công nghệ phần mềm và nó được ứng dụng để phát triển phần mềm trong
nhiều lĩnh vực khác nhau. Trong số đó, ứng dụng quan trọng và nổi tiếng nhất hiện nay là
thiết kế giao diện với người sử dụng. kiểu như Windows. Các hệ thống tin quản lý trong
thực tế thường rất phức tạp, chứa nhiều đối tượng với nhiều thuộc tính và hàm phức tạp.
Để giải quyết những hệ thống thông tin phức tạp như thế , lập trình hướng đối tượng tỏ ra
rất hiệu quả. Các lĩnh vực ứng dụng phù hợp với kỹ thuật lập trình hướng đối tượng có
thể liệt kê như dưới đây:
 Các hệ thống làm việc theo thời gian thực
 Các hệ mô hình hoá hoặc mô phỏng các quá trình
 Các hệ cơ sở dữ liệu hướng đối tượng
Đề cương Lập trình hướng đối tượng với C# Trang 30
 Các hệ siêu văn bản, đa phương tiện
 Các hệ thống trí tuệ nhân tạo và hệ chuyên gia
 Các hệ thống song song và mạng nơ-ron
 Các hệ tự động hoá văn phòng hoặc trợ giúp quyết định  Các hệ CAD/CAM.
1.3.3. Ngôn ngữ lập trình C#
C# là một ngôn ngữ lập trình hướng đối tượng được phát triển bởi Microsoft, là
phần khởi đầu cho kế hoạch .NET của họ. Tên của ngôn ngữ bao gồm ký tự thăng theo
Microsoft nhưng theo ECMA là C#, chỉ bao gồm dấu số thường. Microsoft phát triển C#
dựa trên C++ và Java. C# được miêu tả là ngôn ngữ có được sự cân bằng giữa C++,
Visual Basic, Delphi và Java.
C#, theo một hướng nào đó, là ngôn ngữ lập trình phản ánh trực tiếp nhất đến
.NET Framework mà tất cả các chương trình .NET chạy, và nó phụ thuộc mạnh mẽ vào
Framework này. Các loại dữ liệu cơ sở là những đối tượng, hay được gọi là garbage-
collected, và nhiều kiểu trừu tượng khác chẳng hạn như class, delegate, interface,
exception, v.v, phản ánh rõ ràng những đặc trưng của .NET runtime.
So sánh với C và C++, ngôn ngữ này bị giới hạn và được nâng cao ở một vài đặc
điểm nào đó, nhưng không bao gồm các giới hạn sau đây:
Các con trỏ chỉ có thể được sử dụng trong chế độ không an toàn. Hầu hết các đối
tượng được tham chiếu an toàn, và các phép tính đều được kiểm tra tràn bộ đệm. Các con
trỏ chỉ được sử dụng để gọi các loại kiểu giá trị; còn những đối tượng thuộc bộ thu rác
(garbage-collector) thì chỉ được gọi bằng cách tham chiếu.
+ Các đối tượng không thể được giải phóng tường minh.
+ Chỉ có đơn kế thừa, nhưng có thể cài đặt nhiều interface trừu tượng (abstract
interfaces). Chức năng này làm đơn giản hóa sự thực thi của thời gian thực thi.
+ C# thì an-toàn-kiểu (typesafe) hơn C++.
+ Cú pháp khai báo mảng khác nhau("int[] a = new int[5]" thay vì "int a[5]").
+ Kiểu thứ tự được thay thế bằng tên miền không gian (namespace).
Đề cương Lập trình hướng đối tượng với C# Trang 31 + C# không có tiêu bản.
+ Có thêm Properties, các phương pháp có thể gọi các Properties để truy cập dữ liệu. + Có reflection.
Đề cương Lập trình hướng đối tượng với C# Trang 32
Bài 2: Lớp và đối tượng( 1) 2.1. Lớp
2.1.1. Định nghĩa lớp
Chúng ta đã được nghiên cứu ngôn ngữ lập trình C# trong các học phần trước và
đã thảo luận rất nhiều kiểu dữ liệu cơ bản của ngôn ngữ C# như: int, long và char... Tuy
nhiên trái tim và linh hồn của C# là khả năng tạo ra những kiểu dữ liệu mới, phức tạp.
Người lập trình tạo ra các kiểu dữ liệu mới bằng cách xây dựng các lớp đối tượng và đó
cũng chính là các vấn đề chúng ta cần thảo luận trong chương này.
Đây là khả năng để tạo ra những kiểu dữ liệu mới, một đặc tính quan trọng của
ngôn ngữ lập trình hướng đối tượng. Chúng ta có thể xây dựng những kiểu dữ liệu mới
trong ngôn ngữ C# bằng cách khai báo và định nghĩa những lớp. Ngoài ra ta cũng có thể
định nghĩa các kiểu dữ liệu với những giao diện (interface) sẽ được bàn trong các phần
sau. Thể hiện của một lớp được gọi là những đối tượng (object). Những đối tượng này
được tạo trong bộ nhớ khi chương trình được thực hiện.
Sự khác nhau giữa một lớp và một đối tượng cũng giống như sự khác nhau giữa
khái niệm giữa loài mèo và một con mèo Mun đang nằm bên chân của ta. Chúng ta
không thể đụng chạm hay đùa giỡn với khái niệm mèo nhưng có thể thực hiện điều đó
được với mèo Mun, nó là một thực thể sống động, chứ không trừu tượng như khái niệm
họ loài mèo. Một họ mèo mô tả những con mèo có các đặc tính: có trọng lượng, có chiều
cao, màu mắt, màu lông,...chúng cũng có hành động như là ăn ngủ, leo trèo,...một con
mèo, ví dụ như mèo Mun chẳng hạn, nó cũng có trọng lượng xác định là 5 kg, chiều cao
15 cm, màu mắt đen, lông đen...Nó cũng có những khả năng như ăn ngủ leo trèo,..
Lợi ích to lớn của những lớp trong ngôn ngữ lập trình là khả năng đóng gói các
thuộc tính và tính chất của một thực thể trong một khối đơn, tự có nghĩa, tự khả năng duy trì .
Ví dụ: Khi chúng ta muốn sắp nội dung những thể hiện hay đối tượng của lớp điều
khiển ListBox trên Windows, chỉ cần gọi các đối tượng này thì chúng sẽ tự sắp xếp, còn
việc chúng làm ra sao thì ta không quan tâm, và cũng chỉ cần biết bấy nhiêu đó thôi.
Đóng gói cùng với đa hình (polymorphism) và kế thừa (inheritance) là các thuộc tính
chính yếu của bất kỳ một ngôn ngữ lập trình hướng đối tượng nào.
Đề cương Lập trình hướng đối tượng với C# Trang 33
Trong chương này chúng ta tìm hiểu các đặc tính của ngôn ngữ lập trình C# để
xây dựng các lớp đối tượng. Thành phần của một lớp, các hành vi và các thuộc tính, được
xem như là thành viên của lớp (class member). Tiếp theo là khái niệm về phương
thức(method) được dùng để định nghĩa hành vi của một lớp, và trạng thái của các biến
thành viên hoạt động trong một lớp. Một đặc tính mới mà ngôn ngữ C# đưa ra để xây
dựng lớp là khái niệm thuộc tính (property), thành phần thuộc tính này hoạt động giống
như cách phương thức để tạo một lớp, nhưng bản chất của phương thức này là tạo một
lớp giao diện cho bên ngoài tương tác với biến thành viên một cách gián tiếp, ta sẽ bàn
sâu vấn đề này trong chương.
Định nghĩa lớp: Để định nghĩa một kiểu dữ liệu mới hay một lớp đầu tiên phải khai báo
rồi sau đó mới định nghĩa các thuộc tính và phương thức của kiểu dữ liệu đó. Khai báo
một lớp bằng cách sử dụng từ khoá class. Cú pháp đầy đủ của khai báo một lớp như sau:
[Thuộc tính] [Bổ sung truy cập] class <Định danh lớp> [: Lớp cơ sở] {
phương thức hành động > }
Thành phần thuộc tính của đối tượng sẽ được trình bày chi tiết trong các phần sau
Thành phần bổ sung truy cập cũng sẽ được trình bày tiếp ngay mục dưới.
Định danh lớp chính là tên của lớp do người xây dựng chương trình tạo ra.
Lớp cơ sở là lớp mà đối tượng sẽ kế thừa để phát triển ta sẽ bàn sau.
Tất cả các thành viên của lớp được định nghĩa bên trong thân của lớp, phần thân
này sẽ được bao bọc bởi hai dấu ({}).
Trong C#, mọi chuyện đều xảy ra trong một lớp. Như các ví dụ mà chúng ta đã
tìm hiểu, các hàm điều được đưa vào trong một lớp, kể cả hàm đầu vào của chương trình (hàm Main()):
public class Tester
Đề cương Lập trình hướng đối tượng với C# Trang 34 {
public static int Main()
{ // ... } }
Điều cần nói ở đây là chúng ta chưa tạo bất cứ thể hiện nào của lớp, tức là tạo đối
tượng cho lớp Tester. Điều gì khác nhau giữa một lớp và thể hiện của lớp? để trả lới cho
câu hỏi này chúng ta bắt đầu xem xét sự khác nhau giữa kiểu dữ liệu int và một biến kiểu int . Ta có viết như sau: int var1 = 10;
tuy nhiên ta không thể viết được int = 10;
Ta không thể gán giá trị cho một kiểu dữ liệu, thay vào đó ta chỉ được gán dữ liệu
cho một đối tượng của kiểu dữ lịêu đó, trong trường hợp trên đối tượng là biến var1. Khi
chúng ta tạo một lớp mới, đó chính là việc định nghĩa các thuộc tính và hành vi của tất cả
các đối tượng của lớp. Giả sử chúng ta đang lập trình để tạo các điều khiển trong các ứng
dụng trên Windows, các điều khiển này giúp cho người dùng tương tác tốt với Windows,
như là ListBox, TextBox, ComboBox,...Một trong những điều khiển thông dụng là
ListBox, điều khiển này cung cấp một danh sách liệt kê các mục chọn và cho phép người
dùng chọn các mục tin trong đó. ListBox này cũng có các thuộc tính khác nhau như:
chiều cao, bề dày, vị trí, và màu sắc thể hiện và các hành vi của chúng như: chúng có thể
thêm bới mục tin, sắp xếp,...
Ngôn ngữ lập trình hướng đối tượng cho phép chúng ta tạo kiểu dữ liệu mới là lớp
ListBox, lớp này bao bọc các thuộc tính cũng như khả năng như: các thuộc tính height,
width, location, color, các phương thức hay hành vi như Add(), Remove(), Sort(),...
Chúng ta không thể gán dữ liệu cho kiểu ListBox, thay vào đó đầu tiên ta phải tạo một
đối tượng cho lớp đó:
ListBox myListBox;
Một khi chúng ta đã tạo một thể hiện của lớp ListBox thì ta có thể gán dữ liệu cho thể
hiện đó. Tuy nhiên đoạn lệnh trên chưa thể tạo đối tượng trong bộ nhớ được, ta sẽ bàn
Đề cương Lập trình hướng đối tượng với C# Trang 35
tiếp. Bây giờ ta sẽ tìm hiểu cách tạo một lớp và tạo các thể hiện thông qua ví dụ minh họa
2.1a. Ví dụ này tạo một lớp có chức năng hiểu thị thời gian trong một ngày. Lớp này có
hành vi thể hiện ngày, tháng, năm, giờ, phút, giây hiện hành. Để làm được điều trên thì
lớp này có 6 thuộc tính hay còn gọi là biến thành viên, cùng với một phương thức như sau:
Ví dụ 2.1a: Tạo một lớp Point đơn giản như sau. using System; public class Point { private int x; private int y;
public void Init(int ox,int oy) { x = ox; y = oy; }
public void Move(int dx, int dy) { x += dx; y += dy; } public void Display() {
Console.WriteLine("Toa do la:({0},{1})",x,y); } } public class Tester { static void Main() { Point t = new Point(); t.Init(2, 3); t.Display();
Đề cương Lập trình hướng đối tượng với C# Trang 36 t.Move(-2, 1); t.Display(); } } Kết quả: Toa do:(2,3) Toa do:(0,4)
2.1.2 Thành phần dữ liệu
Cách khai báo các thành phần dữ liệu giống như khai báo biến:
[Thuộc tính] [Bổ sung truy cập] Kieudulieu Tenthanhphan;
Kieudulieu có thể là các kiểu dữ liệu cơ sở (int, float, char, double .... ), các kiểu dữ liệu
do người dùng định nghĩa(struct,union,...) hoặc đối tượng thuộc một lớp đã định nghĩa trước đó. Chú ý:
Không được dùng trực tiếp các lớp để khai báo kiểu thành phần dữ liệu thuộc vào bản
thân lớp đang được định nghĩa.
Các biến thành viên có thể được khởi tạo trực tiếp khi khai báo trong quá trình khởi
tạo, thay vì phải thực hiện việc khởi tạo các biến trong bộ khởi dựng. Để thực hiện việc
khởi tạo này rất đơn giản là việc sử dụng phép gán giá trị cho một biến: int x = 2; // Khởi tạo int y = 3; // Khởi tạo
Việc khởi tạo biến thành viên sẽ rất có ý nghĩa, vì khi xác định giá trị khởi tạo như vậy
thì biến sẽ không nhận giá trị mặc định mà trình biên dịch cung cấp. Khi đó nếu các biến
này không được gán lại trong các phương thức khởi dựng thì nó sẽ có giá trị mà ta đã khởi tạo.
Đề cương Lập trình hướng đối tượng với C# Trang 37
Ví dụ 2.1b: Minh hoạ sử dụng khởi tạo biến thành viên. using System; class ThoiGian { int nam=1900; int thang=1; int ngay=1; int gio=0; int phut=0; int giay=0;
public void KhoiTao(DateTime t) { nam = t.Year; thang = t.Month; ngay = t.Day; gio = t.Hour; phut = t.Minute; giay = t.Second; } public void Hien() {
Console.WriteLine("{0}:{1}:{2} ngay {3}/{4}/{5}",
gio, phut, giay, ngay, thang, nam); } } public class Tester { static void Main() { ThoiGian t = new ThoiGian();
Console.Write(“Thoi gian:”); t.Hien(); t.KhoiTao(DateTime.Now);
Console.Write(“Hien tai la:”); t.Hien(); }
Đề cương Lập trình hướng đối tượng với C# Trang 38 } Kết quả:
Thoi gian: 0:0:0 ngay 1/1/1900
Hien tai la: 11:01:25 ngay 31/01/2007
Nếu không khởi tạo giá trị của biến thành viên thì bộ khởi dựng mặc định sẽ khởi tạo giá
trị là 0 mặc định cho biến thành viên có kiểu nguyên.
2.1.3 Phương thức
Hàm được khai báo trong định nghĩa của lớp được gọi là hàm thành phần hay phương
thức của lớp. Các hàm thành phần có thể truy nhập đến các thành phần dữ liệu và các
hàm thành phần khác trong lớp và các hàm thành phần của lớp khác nếu được phép.
2.1.4. Các từ khoá đặc tả truy cập
Thuộc tính truy cập quyết định khả năng các phương thức của lớp bao gồm việc
các phương thức của lớp khác có thể nhìn thấy và sử dụng các biến thành viên hay những
phương thức bên trong lớp. Bảng 2.1.5 tóm tắt các thuộc tính truy cập của một lớp trong C#. Thuộc tính Giới hạn 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ởi bất kì trong các phương thức của các lớp khác. private
Thành viên trong một lớp A được đánh dấu là private thì chỉ được
truy cập bới các phương thức của lớp A. protected
Thành viên trong lớp A được đánh dấu là protected thì chỉ được các
phương thức bên trong lớp A và những phương thức của lớp dẫn
xuất từ lớp A truy cập. internal
Thành viên trong lớp A được đánh dấu là internal thì được truy cập
bởi bất cứ lớp nào trong cùng khối hợp ngữ với A
Đề cương Lập trình hướng đối tượng với C# Trang 39 protected internal
Thành viên trong lớp A được đánh dấu là protected internal được
truy cập bởi các phương thức của lớp A, các phương thức của lớp
dẫn xuất của lớp A, và bất cứ lớp nào trong cùng khối hợp ngữ với lớp A.
Mong muốn chung là thiết kế các biến thành viên của lớp ở thuộc tính private.
Khi đó chỉ có phương thức thành viên của lớp truy cập được giá trị của biến. C# xem
thuộc tính private là mặc định nên trong ví dụ 2.1b ta không khai báo thuộc tính truy cập
cho 6 biến nên mặc định chúng là private:
// Các biến thành viên private int Nam; int Thang; int Ngay; int Gio; int Phut; int Giay;
Trong lớp ThoiGian có hai phương thức thành viên Hien và KhoiTao được khai
báo là public nên bất kỳ lớp nào cũng có thể truy cập được. Chú ý:
Thói quen lập trình tốt là khai báo tường minh các thuộc tính truy cập của biến
thành viên hay các phương thức trong một lớp. Mặc dù chúng ta biết chắc chắn rằng các
thành viên của lớp là được khai báo private mặc định. Việc khai báo tường minh này sẽ
làm cho chương trình dễ hiểu, rõ ràng và tự nhiên hơn.
Các thành phần trong khái báo lớp được sắp xếp hết sức tùy ý. Nhưng chúng ta
lên gop các thành phần dữ liệu vào một chỗ, các phương thức vào một chỗ
Ví dụ 2.1.5: Nhập vào độ dài ba cạnh của một tam giác rồi tính diện tích của tam giác và
cho biết đó là tam giác gì
Đề cương Lập trình hướng đối tượng với C# Trang 40 using System; public class TamGiac {
private double a, b, c;/*độ dài ba cạnh*/ public void nhap() {
/*nhập vào ba cạnh của tam giác, có kiểm tra điều kiện*/ do {
Console.Write("Nhap vao canh a=");
a = Convert.ToDouble(Console.ReadLine());
Console.Write("Nhap vao canh b=");
b = Convert.ToDouble(Console.ReadLine());
Console.Write("Nhap vao canh c=");
c = Convert.ToDouble(Console.ReadLine());
} while (a + b <= c || b + c <= a || c + a <= b); } public void hien() {
Console.WriteLine("Do dai ba canh:a={0},b={1},c={2}", a, b, c);
/* gọi hàm thành phần bên trong một hàm thành phần khác cùng lớp */
Console.WriteLine("Dien tich tam giac : {0}", dientich()); switch (loaitg()) {
case 1: Console.WriteLine("Tam giac deu"); break;
case 2: Console.WriteLine("Tam giac vuong can"); break;
case 3: Console.WriteLine("Tam giac can"); break;
case 4: Console.WriteLine("Tam giac vuong"); break;
default: Console.WriteLine("Tam giac thuong"); break; } }
private double dientich() {
return (0.25 *Math.Sqrt((a + b + c) * (a + b - c) * (a - b + c) * (-a + b + c)));
Đề cương Lập trình hướng đối tượng với C# Trang 41 } private int loaitg() {
if (a == b || b == c || c == a) if (a == b && b == c) return 1;
else if (a * a == b * b + c * c || b * b ==a * a + c * c || c * c == a * a + b * b) return 2; else return 3;
else if (a * a == b * b + c * c || b * b ==a * a + c * c || c * c == a * a + b * b) return 4; else return 5; } } public class Tester { static void Main() { TamGiac t=new TamGiac (); t.nhap(); t.hien(); } }
2.1.5. Khai báo đối tượng, mảng đối tượng
Một lớp sau khi được định nghĩa có thể xem như là một kiểu dữ liệu đối tượng và có
thể dùng để khai báo các biến, mảng đối tượng. Cách khai báo biến, mảng đối tượng
giống như khai báo các biến bình thường khác:
C1: Định _danh _lớp Tên_đối_tượng = new Định_danh_lớp([tham số]);
C2: Định _danh _lớp Tên_đối_tượng;
…………………………
Tên_đối_tượng =new Định_danh_lớp([tham số]);
Đề cương Lập trình hướng đối tượng với C# Trang 42
Ví dụ: Point A, B; // Khai báo hai đối tượng A,B A= new Point();
Point []a = new Point[5]; // Khai báo mảng con trỏ đối tượng
// Xin cấp phát vùng nhớ cho tùng con trỏ đối tượng và gán giá trị cho các thành phần dữ liệu
for (int i = 0; i < a.Length; ++i) { a[i] = new Point(); }
Khi khái báo một đối tượng máy sẽ cung cấp vùng dữ liệu riêng cho từng đối tượng (
các đối tượng khác hay các hàm bên ngoài không được truy xuất nêu không cho phép)
và các đối tượng dùng chung nhau định nghĩa hàm. Lớp Khung DL
2.1.6. Định nghĩa chồng phương thức
Ta biết rằng trong một số ngôn ngữ lập trình như: Pascal, C,..Khi chúng ta định nghĩa
các hàm thì các hàm phải khác tên nhau. Trong C# cho phép chúng ta định nghĩa các
phương thức trùng tên nhau. Ta gọi đó là định nghĩa chồng phương thức. Khi các phương
thức được định nghĩa trùng tên nhau thì giữa các phương thức phải khác nhau về kiểu giá
trị trả về, số lượng đối và kiểu của các đối. Ta thấy rằng sự khác nhau đó là cần thiết để
khi dịch chương trình thì chương trình dịch căn cứ vào đó mà phân biết giữa các hàm tùy
thuộc vào đối mà chúng ta truyền vào cho hàm.
Đề cương Lập trình hướng đối tượng với C# Trang 43
Ví dụ 2.1.6: Xây dựng chương trình tìm giá trị của một dãy số nguyên using System; public class TimMax { int n; int[] a; public void KhoiTao() {
Console.Write("Nhap so phan tu cua mang n =");
n = Convert.ToInt16(Console.ReadLine()); a = new int[n];
for (int i = 0; i < n; ++i) { Console.Write("a[{0}]=", i);
a[i] = Convert.ToInt16(Console.ReadLine()); } }
public void KhoiTao(int on) { n=on;
Console.WriteLine("So phan tu cua mang n={0}", n); a = new int[n];
for (int i = 0; i < n; ++i) {
Console.Write("a[{0}]=", i); a[i] = Convert.ToInt16(Console.ReadLine()); } }
public int Max(int x, int y) { return (x > y ? x : y); } public int Max()
Đề cương Lập trình hướng đối tượng với C# Trang 44 { int tg = a[0];
for (int i = 1; i < n; ++i) tg = Max(tg, a[i]); return tg; } } public class Tester { static void Main() { TimMax m = new TimMax();
m.KhoiTao(); Console.WriteLine("Max cua day la:{0}", m.Max());
m.KhoiTao(5); Console.WriteLine("Max cua day la:{0}", m.Max()); } } Chú ý:
Một phương thức có thể gọi đến một phương thức cùng tên với nó.
Trong trường hợp có các hàm trùng tên trong chương trình, việc xác định
phương thức nào được gọi do chương trình dịch đảm nhiệm căn cứ vào đối tương
ứng mà chúng ta truyền vào.
2.1.7. Thuộc tính và thủ tục thuộc tính
Thuộc tính là khái niệm cho phép truy cập trạng thái của lớp thay vì thông qua
truy cập trực tiếp các biến thành viên, nó sẽ đựơc thay thế bằng việc thực thi truy cập
thông qua phương thức của lớp.
Đây thật sự là một điều lý tưởng. Các thành phần bên ngoài (client) muốn truy cập
trạng thái của một đối tượng và không muốn làm việc với những phương thức. Tuy
nhiên, người thiết kế lớp muốn dấu trạng thái bên trong của lớp mà anh ta xây dựng, và
cung cấp một cách gián tiếp thông qua một phương thức. Thuộc tính là một đặc tính mới
Đề cương Lập trình hướng đối tượng với C# Trang 45
được giới thiệu trong ngôn ngữ C#. Đặc tính này cung cấp khả năng bảo vệ các trường dữ
liệu bên trong một lớp bằng việc đọc và viết chúng thông qua thuộc tính. Trong ngôn ngữ
khác, điều này có thể được thực hiện thông qua việc tạo các phương thức lấy dữ liệu
(getter method) và phương thức thiết lập dữ liệu (setter method).
Thuộc tính được thiết kế nhắm vào hai mục đích: cung cấp một giao diện đơn cho
phép truy cập các biến thành viên, Tuy nhiên cách thức thực thi truy cập giống như
phương thức trong đó các dữ liệu được che dấu, đảm bảo cho yêu cầu thiết kế hướng đối
tượng. Để hiểu rõ đặc tính này ta sẽ xem ví dụ 2.1.7 bên dưới:
Ví dụ 2.1.7: Sử dụng thuộc tính. using System; public class Time {
public void DisplayCurrentTime() {
Console.WriteLine("Time\t: {0}/{1}/{2} {3}:{4}:{5}",date, month, year, hour, minute, second); }
public Time( System.DateTime dt) { year = dt.Year; month = dt.Month; date = dt.Day; hour = dt.Hour; minute = dt.Minute; second = dt.Second; } public int Hour { get { return hour; }
Đề cương Lập trình hướng đối tượng với C# Trang 46 set { hour = value; } }
// Biến thành viên private private int year; private int month; private int date; private int hour; private int minute; private int second; } public class Tester { static void Main() {
DateTime currentTime = System.DateTime.Now;
Time t = new Time( currentTime ); t.DisplayCurrentTime();
// Lấy dữ liệu từ thuộc tính Hour int theHour = t.Hour;
Console.WriteLine("Retrieved the hour: {0}", theHour); theHour++; t.Hour = theHour;
Console.WriteLine("Updated the hour: {0}", theHour); } } Kết quả: Time : 31/1/2007 17:55:1 Retrieved the hour: 17 Updated the hour: 18
Đề cương Lập trình hướng đối tượng với C# Trang 47
Để khai báo thuộc tính, đầu tiên là khai báo tên thuộc tính để truy cập, tiếp theo là
phần thân định nghĩa thuộc tính nằm trong cập dấu ({}). Bên trong thân của thuộc tính là
khai báo hai bộ truy cập lấy và thiết lập dữ liệu: public int Hour { get { return hour; } set { hour = value; } }
Mỗi bộ truy cập được khai báo riêng biệt để làm hai công việc khác nhau là lấy
hay thiết lập giá trị cho thuộc tính. Giá trị thuộc tính có thể được lưu trong cơ sở dữ liệu,
khi đó trong phần thân của bộ truy cập sẽ thực hiện các công việc tương tác với cơ sở dữ
lịêu. Hoặc là giá trị thuộc tính được lưu trữ trong các biến thành viên của lớp như trong ví
dụ: private int hour;
Truy cập lấy dữ liệu (get accessor)
Phần khai báo tương tự như một phương thức của lớp dùng để trả về một đối
tượng có kiểu dữ liệu của thuộc tính. Trong ví dụ trên, bộ truy cập lấy dữ liệu get của
thuộc tính Hour cũng tương tự như một phương thức trả về một giá trị int. Nó trả về giá
trị của biến thành viên hour nơi mà giá trị của thuộc tính Hour lưu trữ: get { return hour; }
Trong ví dụ này, một biến thành viên cục bộ được trả về, nhưng nó cũng có thể truy cập
dễ dàng một giá trị nguyên từ cơ sở dữ lịêu, hay thực hiện việc tính toán tùy ý. Bất cứ khi
nào chúng ta tham chiếu đến một thuộc tính hay là gán giá trị thuộc tính cho một biến thì
bộ truy cập lấy dữ liệu get sẽ được thực hiện để đọc giá trị của thuộc tính:
Đề cương Lập trình hướng đối tượng với C# Trang 48
Time t = new Time( currentTime ); int theHour = t.Hour;
Khi lệnh thứ hai được thực hiện thì giá trị của thuộc tính sẽ được trả về, tức là bộ truy cập
lấy dữ lịêu get sẽ được thực hiện và kết quả là giá trị của thuộc tính được gán cho biến cục bộ theHour.
Bộ truy cập thiết lập dữ liệu ( set accessor)
Bộ truy cập này sẽ thiết lập một giá trị mới cho thuộc tính và tương tự như một
phương thức trả về một giá trị void. Khi định nghĩa bộ truy cập thiết lập dữ lịêu chúng ta
phải sử dụng từ khóa value để đại diện cho tham số được truyền vào và được lưu trữ bởi thuộc tính: set { hour = value; }
Như đã nói trước, do ta đang khai báo thuộc tính lưu trữ dưới dạng biến thành viên
nên trong phần thân của bộ truy cập ta chỉ sử dụng biến thành viên mà thôi. Bộ truy cập
thiết lập hoàn toàn cho phép chúng ta có thể viết giá trị vào trong cơ sở dữ lịêu hay cập
nhật bất cứ biến thành viên nào khác của lớp nếu cần thiết.
Khi chúng ta gán một giá trị cho thuộc tính thì bộ truy cập thiết lập dữ liệu set sẽ
được tự động thực hiện và một tham số ngầm định sẽ được tạo ra để lưu giá trị mà ta muốn gán: theHour++; t.Hour = theHour;
Lợi ích của hướng tiếp cận này cho phép các thành phần bên ngoài (client) có thể tương
tác với thuộc tính một cách trực tiếp, mà không phải hy sinh việc che dấu dữ lịêu cũng
như đặc tính đóng gói dữ lịêu trong thiết kế hướng đối tượng.
2.1.8. Từ khoá this
Như chúng ta biết rằng trong lập trình hướng đối tượng thì bài toán được chia
thành các đối tượng. Trong mỗi đối tượng có các thành phần dữ liệu các phương thức.
Đề cương Lập trình hướng đối tượng với C# Trang 49
Khi nói tới các thành phần dữ liệu và các phương thức thì phải gắn nó với một đối tượng
cụ thể. Ta xem lại lớp Point trong phần trước:
Ví dụ: Trong lớp Point trong các phần trước ta có
public void Init(int ox, int oy) { x=ox; y=oy; }
Thoạt nhìn ta thấy trong phương thức Init của lớp Point các thành phần dữ liệu
được sử dụng không gắn với đối tượng cụ thể nào. Ta thấy có vẻ không phù hợp với
nguyên tắc của lập trình hướng đối tượng. Nhưng thực tế không phải thế. Trong C# sử
dụng một đối đặc biệt this thong các phương thức(đối này là một đối ngầm định). Các
thuộc tính viết trong phương thức được hiểu là thuộc một đối tượng của this. Ta có thể
viết lại phương thức trên như sau:
public void Init(int ox, int oy) { this.x=ox; this.y=oy; }
Từ đó ta thấy rằng: Phương thức bao giờ cũng có ít nhất một đối là this và nó luôn là
đối đầu tiên của phương thức.
Xét một lời gọi tới phương thức Init(...) Point A=new Point(); A.Init(3,4);
Trong trường hợp này tham số truyền cho this chính đối tượng A.
Do đó: this.x chính là A.x...
Tóm lại: Tham số truyền cho đối this chính là đối tượng đi kèm với phương thức trong lời gọi phương thức.
Ví dụ 2.1.8: Xây dựng chương trình tính tổng hai phân số using System; public class PS
Đề cương Lập trình hướng đối tượng với C# Trang 50 { private int tuso; private int mauso; public void nhap() {
Console.Write("Nhap tu so:"); tuso = Convert.ToInt16(Console.ReadLine());
Console.Write("Nhap mau so:"); mauso = Convert.ToInt16(Console.ReadLine()); } public void hien() { this.rutgon(); if (mauso == 1)
Console.WriteLine("{0}", tuso); else
Console.WriteLine("{0}/{1}", tuso, mauso); }
public int uscln(int x, int y) { x = Math.Abs(x); y = Math.Abs(y); while (x != y) { if (x > y) x -= y; if (y > x) y -= x; } return x; } public void rutgon() { int uc = uscln(tuso, mauso); tuso = tuso / uc; mauso = mauso / uc; if(tuso*mauso>0) {
Đề cương Lập trình hướng đối tượng với C# Trang 51
tuso=Math.Abs(tuso);mauso=Math.Abs(mauso); } else if(mauso<0) { tuso=-1*tuso; mauso= Math.Abs(mauso); } } public PS tong(PS b) { PS tmp=new PS();
tmp.tuso = this.tuso*b.mauso+this.mauso*b.tuso;
tmp.mauso = this.mauso*b.mauso; tmp.rutgon(); return tmp; } } public class Tester { static void Main() { PS a = new PS(); PS b = new PS(); a.nhap(); b.nhap(); a.tong(b).hien(); } }
Đề cương Lập trình hướng đối tượng với C# Trang 52
Bài 3: Lớp và đối tượng( 2)
3.1. Phép gán các đối tượng
Như chúng ta đã biết trong C# có hai loại kiểu dữ liệu đó là kiểu dữ liệu giá trị và kiểu
dữ liệu tham chiếu: Các kiểu dữ liệu giá trị đó là các kiểu dữ liệu xây dựng sẵn của C#
như: int, float,char... trừ kiểu dữ liệu xâu. Các kiểu dữ liệu tham chiếu đó là các kiểu dữ
liệu do người dùng định nghĩa như: đối tượng, mảng,...trừ kiểu dữ liệu cấu trúc.
Đối với kiểu dữ liệu giá trị thì sẽ được lưu kích thước thật trong bộ nhớ đã cấp phát là
stack. Trong khi đó thì địa chỉ của kiểu dữ liệu tham chiếu thì được lưu trong stack nhưng
đối tượng thực sự thì lưu trong bộ nhớ heap
Chính vì vậy khi ta gán hai đối tượng cho nhau thì điều gì sẽ diễn ra.
Xét ví dụ sau: Trong phân trên ta có xây dựng lớp Point ta có thể khai báo như sau:
Point a=new Point(); a.Dispaly();
Point b=new Point(); b.Init(6,3); a=b;
Ta có thể mô hình điều này như sau:
Ta nhận thấy rằng sau khi gán đối tượng b vào đối tượng a thì ta thấy rằng hai đối tượng
a, b cùng sử dụng chung vùng nhớ. Điều này phá vỡ nguyên tắc của lập trình hướng đối tượng.
3.2. Phương thức thiết lập và sao chép
3.2.1. Phương thức thiết lập
Đề cương Lập trình hướng đối tượng với C# Trang 53
Đặc điểm của phương thức thiết lập
Thử xem lại ví dụ minh họa 2.1b, câu lệnh tạo một đối tượng cho lớp ThoiGian
tương tự như việc gọi thực hiện một phương thức: ThoiGian t = new ThoiGian();
Đúng như vậy, một phương thức sẽ được gọi thực hiện khi chúng ta tạo một đối tượng.
Phương thức này được gọi là bộ khởi dựng hay phương thức thiết lập (constructor). Các
phương thức này được định nghĩa khi xây dựng lớp, nếu ta không tạo ra thì CLR sẽ thay
mặt chúng ta mà tạo phương thức khởi dựng một cách mặc định. Chức năng của bộ khởi
dựng là tạo ra đối tượng được xác định bởi một lớp và đặt trạng thái này hợp lệ. Trước
khi bộ khởi dựng được thực hiện thì đối tượng chưa được cấp phát trong bộ nhớ. Sau khi
bộ khởi dựng thực hiện hoàn thành thì bộ nhớ sẽ lưu giữ một thể hiện hợp lệ của lớp vừa khai báo.
Lớp ThoiGian trong ví dụ 2.1b không định nghĩa bộ khởi dựng. Do không định
nghĩa nên trình biên dịch sẽ cung cấp một bộ khởi dựng cho chúng ta. Phương thức khởi
dựng mặc định được tạo ra cho một đối tượng sẽ không thực hiện bất cứ hành động nào,
tức là bên trong thân của phương thức rỗng. Các biến thành viên được khởi tạo các giá trị
tầm thường như thuộc tính nguyên có giá trị là 0 và chuỗi thì khởi tạo rỗng,..Bảng 2.3.1
sau tóm tắt các giá trị mặc định được gán cho các kiểu dữ liệu cơ bản. Kiểu dữ liệu
Giá trị mặc định int, long, byte,… 0 bool false char ‘\0’ (null) enum 0 reference null
Bảng 2.3.1: Giá trị mặc định của kiểu dữ liệu cơ bản.
Thường thường, khi muốn định nghĩa một phương thức khởi dựng riêng ta phải cung cấp
các tham số để hàm khởi dựng có thể khởi tạo các giá trị khác ngoài giá trị mặc định cho
Đề cương Lập trình hướng đối tượng với C# Trang 54
các đối tượng. Quay lại ví dụ 2.1b giả sử ta muốn truyền thời gian hiện hành: năm, tháng,
ngày,…để đối tượng có ý nghĩa hơn.
Một phương thức thiết lập được định nghĩa tường minh có các đặc điểm sau:
1. Phương thức thiết lập có cùng tên với tên của lớp.
2. Phương thức thiết lập thường có thuộc tính public.
3. Phương thức thiết lập không có giá trị trả về và không cần khai báo void.
4. Có thể có nhiều phương thức thiết lập trong cùng lớp (chồng các phương thức thiết lập).
5. Khi một lớp có nhiều phương thức thiết lập, việc tạo các đối tượng phải kèm
theo các tham số phù hợp với một trong các hàm thiết lập đã khai báo.
Ví dụ 2.3.1: Định nghĩa một bộ khởi dựng. using System; public class ThoiGian {
public void ThoiGianHienHanh() {
Console.WriteLine(“ Thoi gian hien hanh la : {0}/{1}/{2} {3}:{4}:{5}”, Ngay, Thang, Nam, Gio, Phut, Giay); }
// Phương thức thiết lập
public ThoiGian( DateTime dt ) { Nam = dt.Year; Thang = dt.Month; Ngay = dt.Day; Gio = dt.Hour; Phut = dt.Minute; Giay = dt.Second; }
// Biến thành viên private int Nam;
Đề cương Lập trình hướng đối tượng với C# Trang 55 int Thang; int Ngay; int Gio; int Phut; int Giay; } public class Tester { static void Main() {
System.DateTime currentTime = System.DateTime.Now;
ThoiGian t = new ThoiGian( currentTime ); t.ThoiGianHienHanh(); } } Kết quả:
Thoi gian hien hanh la: 01/02/2007 9:10:20
Trong ví dụ trên phương thức khởi dựng lấy một đối tượng DateTime và khởi tạo
tất cả các biến thành viên dựa trên giá trị của đối tượng này. Khi phương thức này thực
hiện xong, một đối tượng ThoiGian được tạo ra và các biến của đối tượng cũng đã được
khởi tạo. Hàm ThoiGianHienHanh được gọi trong hàm Main() sẽ hiển thị giá trị thời gian
lúc đối tượng được tạo ra.
Chúng ta thử bỏ một số lệnh khởi tạo trong phương thức khởi dựng và cho thực
hiện chương trình lại thì các biến không được khởi tạo sẽ có giá trị mặc định là 0, do là
biến nguyên. Một biến thành viên kiểu nguyên sẽ được thiết lập giá trị là 0 nếu chúng ta
không gán nó trong phương thức khởi dựng. Chú ý rằng kiểu dữ liệu giá trị không thể
không được khởi tạo, nếu ta không khởi tạo thì trình biên dịch sẽ cung cấp các giá trị mặc định theo bảng 2.3.1
Đề cương Lập trình hướng đối tượng với C# Trang 56
Ngoài ra trong chương trình 2.3.1 trên có sử dụng đối tượng của lớp DateTime,
lớp DateTime này được cung cấp bởi thư viện System, lớp này cũng cung cấp các biến
thành viên public như: Year, Month, Day, Hour, Minute, và Second tương tự như lớp ThoiGian của chúng ta.
Thêm vào đó là lớp này có đưa ra một phương thức thành viên tĩnh tên là Now,
phương thức Now sẽ trả về một tham chiếu đến một thể hiện của một đối tượng
DateTime được khởi tạo với thời gian hiện hành. Theo như trên khi lệnh :
System.DataTime currentTime = System.DateTime.Now();
được thực hiện thì phương thức tĩnh Now() sẽ tạo ra một đối tượng DateTime trên bộ nhớ
heap và trả về một tham chiếu và tham chiếu này được gán cho biến đối tượng currentTime.
Sau khi đối tượng currentTime được tạo thì câu lệnh tiếp theo sẽ thực hiện việc
truyền đối tượng currentTime cho phương thức khởi dựng để tạo một đối tượng ThoiGian:
ThoiGian t = new ThoiGian( currentTime );
Bên trong phương thức khởi dựng này tham số dt sẽ tham chiếu đến đối tượng DateTime
là đối tượng vừa tạo mà currentTime cũng tham chiếu. Nói cách khác lúc này tham số dt
và currentTime cùng tham chiếu đến một đối tượng DateTime trong bộ nhớ. Nhờ vậy
phương thức khởi dựng ThoiGian có thể truy cập được các biến thành viên public của đối tượng
DateTime được tạo trong hàm Main().
Có một sự nhấn mạnh ở đây là đối tượng DateTime được truyền cho bộ dựng
ThoiGian chính là đối tượng đã được tạo trong hàm Main và là kiểu dữ liệu tham chiếu.
Do vậy khi thực hiện truyền tham số là một kiểu dữ liệu tham chiếu thì con trỏ được ánh
xạ qua chứ hoàn toàn không có một đối tượng nào được sao chép lại.
Sử dụng phương thức thiết lập
Đề cương Lập trình hướng đối tượng với C# Trang 57
Như chúng ta đã biết trong một khai báo lớp nếu chúng ta không tạo ra phương
thức thiết lập tường minh thì chương trình dịch tự tạo ra một phương thứ thiết lập không
tham số mỗi khi một đối tượng được tạo ra.
Ví dụ trong ví dụ trước ta có lớp Point khi đó
Point a=new Point();// thì có một phương thức thiết lập không tham số tự
động được tạo ra khi khai báo đối tượng a Xét ví dụ sau: using System; public class Point { int x,y; public Point(int ox, int oy) { x = ox; y = oy; }
public void Move(int dx, int dy) { x += dx; y += dy; } public void Display() {
Console.WriteLine("Toa do la:({0},{1})",x,y); } } public class Tester { static void Main() {
Đề cương Lập trình hướng đối tượng với C# Trang 58 Point []a = new Point[5];
// Khai báo mảng đối tượng con trỏ
// Cấp phát bộ nhớ cho từng phần tử của mảng
for (int i = 0; i < a.Length; ++i) a[i] = new Point();
// Câu lệnh này là không hợp lệ do không có phương
thức thiết lập phù hợp khi tạo ra đối tượng
for (int i = 0; i < a.Length; ++i) a[i].Display(); } }
Để câu lệnh trên đúng thì hoặc chúng ta bỏ phương thức thiết lập hai tham số trong lớp
Point hoặc thêm một phương thức thiết lập không tham số vào lớp Point hoặc đưa thêm
tham số vào câu lệnh trên using System; public class Point { int x,y; public Point(int ox, int oy) { x = ox; y = oy; } public Point() { x = 0; y = 0; }
public void Move(int dx, int dy) { x += dx; y += dy; } int y; public void Display() {
Đề cương Lập trình hướng đối tượng với C# Trang 59
Console.WriteLine("Toa do la:({0},{1})",x,y); } } public class Tester { static void Main() { Point []a = new Point[5];
// Khai báo mảng đối tượng con trỏ a[0] = new Point(2,3);
// Phương thức thiết lập hai tham số được gọi
// Cấp phát bộ nhớ cho từng phần tử của mảng
for (int i = 1; i < a.Length; ++i) a[i] = new Point();
// Câu lệnh này đúng vì lúc này phương thức thiết
lập không tham số được gọi
for (int i = 0; i < a.Length; ++i) a[i].Display(); } }
3.2.2. Phương thức sao chép
Bộ khởi dựng sao chép thực hiện việc tạo một đối tượng mới bằng cách sao chép
tất cả các biến từ một đối tượng đã có và cùng một kiểu dữ liệu. Ví dụ chúng ta muốn
đưa một đối tượng PhanSo vào bộ khởi dựng lớp PhanSo để tạo một đối tượng PhanSo
mới có cùng giá trị với đối tượng PhanSo cũ. Hai đối tượng này hoàn toàn khác nhau và
chỉ giống nhau ở giá trị biến thành viên sau khi khởi dựng.
Ngôn ngữ C# không cung cấp bộ khởi dựng sao chép, do đó chúng ta phải tự tạo
ra. Việc sao chép các thành phần từ một đối tượng ban đầu cho một đối tượng mới như sau: public ClassName(ClassName b) {
// sao chép các giá trị của thành phần đối tượng b cho lớp. } Ví dụ:
Đề cương Lập trình hướng đối tượng với C# Trang 60
public PhanSo(PhanSo t) { this.ts = t.ts; this.ms = t.ms; }
Khi đó ta có thể sao chép từ một đối tượng PhanSo đã hiện hữu như sau: PhanSo a = new PhanSo(n, m); PhanSo b = new PhanSo(a);
Trong đó a là đối tượng PhanSo đã tồn tại, sau khi lệnh trên thực hiện xong thì đối tượng
b được tạo ra như bản sao của đối tượng a.
3.3. Huỷ bỏ đối tượng, cơ chế gom rác trong .Net
Ngôn ngữ C# cung cấp cơ chế thu dọn (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 bởi cơ chế thu dọn khi đối tượng bị hủy.
Phương thức kết thúc 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(). Chúng ta chỉ làm điều này khi xử lý các tài nguyên không kiểm soát được.
Chúng 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 chúng ta. Trình thu dọn sẽ thực hiện việc gọi Finalize() cho chúng ta.
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
Đề cương Lập trình hướng đối tượng với C# Trang 61
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.
Bộ hủy của C#: Cú pháp phương thức hủy trong ngôn ngữ C# cũng giống như trong ngôn ngữ C++.
Nhưng về hành động cụ thể chúng có nhiều điểm khác nhau. 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 liên 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 chúng 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.
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 dành cho bộ thu dọn thực hiện.
Nếu chúng ta 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 và ta
muốn được đóng hay giải phóng nhanh chóng bất cứ lúc nào, ta có thực thi giao diện
IDisposable, phần chi tiết IDisposable sẽ được trình bày chi tiết trong Chương sau. Giao
diện IDisposable yêu cầu những thành phần thực thi của nó định nghĩa một phương thức
Đề cương Lập trình hướng đối tượng với C# Trang 62
tên là Dispose() để thực hiện công việc dọn dẹp mà ta yêu cầu. Ý 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() 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ọc GC trong thực hiện kết thúc GC.SuppressFinalize( this ); }
public override void Finalize() { Dispose(); base.Finalize(); }
Phương thức Close: Khi xây dựng các đối tượng, chúng ta có 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().
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
Đề cương Lập trình hướng đối tượng với C# Trang 63
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.
Ví dụ 2.4: Sử dụng chỉ dẫn using. using System.Drawing; class Tester { public static void Main() {
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 } }
Trong phần khai báo đầu của ví dụ thì đối tượng Font được khai báo bên trong câu
lệnh using. Khi câu lệnh using kết thúc, thì phương thức Dispose của đối tượng Font sẽ được gọi.
Còn trong phần khai báo thứ hai, một đối tượng Font được tạo bên ngoài câu lệnh
using. Khi quyết định dùng đối tượng này ta đặt nó vào câu lệnh using. Và cũng tương tự
như trên khi khối câu lệnh using thực hiện xong thì phương thức Dispose() của font được gọi.
Đề cương Lập trình hướng đối tượng với C# Trang 64
Bài 4: Lớp và đối tượng( 3)
4.1. Thành phần tĩnh và cách sử dụng
4.1.1. Thành phần dữ liệu tĩnh
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
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++.
Trong một số ngôn ngữ thì có sự phân chia giữa phương thức của lớp và các
phương thức khác (toàn cục) tồn tại bên ngoài không phụ thuộc bất cứ một lớp nào. Tuy
nhiên, điều này không cho phép trong C#, ngôn ngữ C# không cho phép tạo các phương
thức bên ngoài của lớp, nhưng ta có thể tạo được các phương thức giống như vậy bằng
cách tạo các phương thức tĩnh bên trong một lớp. 5.1.1 Thành phần dữ liệu tĩnh
Sử dụng các thuộc tính tĩnh
Như ta đã biết khi nói tới một thành phần dữ liệu thì ta phải nghĩ ngay đến nó gắn
với một đối tượng cụ thể nào đó. Trong qua trình lập trình ta muốn có những thành phần
dữ liệu không thuộc bất kỳ đối tượng nào. Để có được điều đó thì thành phần dữ liệu đó
phải là tĩnh bằng cách đặt từ khóa static trước tên thành phần dữ liệu đó. Như vậy một
thành phần dữ liệu tĩnh thì nó được cấp phát một vùng nhớ cố định và nó không phải là
riêng của một đối tượng nào.
Để truy nhập tới một thành phần dữ liệu tĩnh ta dùng tên lớp và không được truy
nhập thông qua tên một đối tượng. Chính vì vậy các thành phần dữ liệu tĩnh chỉ được sử
dụng trong các phương thức tĩnh.
Ví dụ 2.5b: Nhập vào một danh sách cán bộ và cho biết tổng lương của các cán bộ vừa nhập
Đề cương Lập trình hướng đối tượng với C# Trang 65 using System; public class Canbo { string hoten; double luong; public static double tl=0; public void nhap() {
Console.Write("Ho ten:"); hoten = Console.ReadLine();
Console.Write("Luong:"); luong = Convert.ToDouble(Console.ReadLine()); tl = tl + luong; } public void hien(int i) {
Console.WriteLine("{0}\t{1}\t{2}", i, hoten, luong); } } public class DsCanBo { int n; Canbo[] ds; public void nhap() {
Console.Write("Nhap so can bo:"); n = Convert.ToInt16(Console.ReadLine()); ds=new Canbo[n];
for (int i = 0; i < n; ++i) ds[i] = new Canbo();
Console.WriteLine("Nhap thong tin cho cac can bo");
for (int i = 0; i < n; ++i) ds[i].nhap(); } public void hien() {
for (int i = 0; i < n; ++i) ds[i].hien(i + 1); }
Đề cương Lập trình hướng đối tượng với C# Trang 66 } public class Tester { static void Main() { DsCanBo c = new DsCanBo(); c.nhap();
Console.WriteLine("\t\t\tDanh sach cac can bo la\n");
Console.WriteLine("STT\tHo va ten\tLuong"); c.hien();
Console.WriteLine("Tong luong cua cac can bo la:{0}", Canbo.tl); } }
4.1.2. Phương thức tĩnh
Phương thức tĩnh hoạt động ít nhiều giống như phương thức toàn cục, ta truy cập
phương thức này mà không cần phải tạo bất cứ thể hiện hay đối tượng của lớp chứa
phương thức toàn cục. Tuy nhiên, lợi ích của phương thức tĩnh vượt xa phương thức toàn
cục vì phương thức tĩnh được bao bọc trong phạm vi của một lớp nơi nó được định nghĩa,
do vậy ta sẽ không gặp tình trạng lộn xộn giữa các phương thức trùng tên do chúng được đặt trong namespace.
Ghi chú: Chúng ta không nên bị cám dỗ bởi việc tạo ra một lớp chứa toàn bộ các
phương thức linh tinh. Điều này có thể tiện cho công việc lập trình nhưng sẽ điều không
mong muốn và giảm tính ý nghĩa của việc thiết kế hướng đối tượng. Vì đặc tính của việc
tạo các đối tượng là xây dựng các phương thức và hành vi xung quanh các thuộc tính hay
dữ liệu của đối tượng.
Gọi một phương thức tĩnh:
Như chúng ta đã biết phương thức Main() là một phương thức tĩnh. Phương tĩnh
được xem như là phần hoạt động của lớp hơn là của thể hiện một lớp. Chúng cũng không
cần có một tham chiếu this hay bất cứ thể hiện nào tham chiếu tới. Như vậy phương thức
tĩnh không có đối ngầm địh là this. Do vậy phương thức tĩnh không thể truy cập trực tiếp
đến các thành viên không có tính chất tĩnh (nonstatic). Như vậy Main() không thể gọi
một phương thức không tĩnh bên trong lớp.
Đề cương Lập trình hướng đối tượng với C# Trang 67
Để xây dựng một phương thức là tĩnh ta thêm từ khóa static trong định nghĩ phương thức.
Ví dụ 2.5a: Cho P(x)=a[0]xn+a[1]xn-1+..+a[n] P( x) Tính 3 using System; public class DaThuc { int n; double x;
double [] hs = new double [50]; public void nhap() { Console.Write("Nhap he so=");
n = Convert.ToInt16(Console.ReadLine());
Console.Write("Nhap x="); x = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("Nhap cac he so");
for (int i = 0; i <=n; ++i) { Console.Write("hs[{0}]=", i);
hs[i] = Convert.ToDouble(Console.ReadLine()); } }
public static double mu(double x, int n) { if (n == 0) return 1; else return x * mu(x, n - 1); }
public static double can3(double x) { double y;
Đề cương Lập trình hướng đối tượng với C# Trang 68 if (x == 0) y = 0;
else if (x > 0) y = Math.Exp(1.0 / 3 * Math.Log(x));
else y = -Math.Exp(1.0 / 3 * Math.Log(-x)); return x; } public double gtdt() { double p = 0;
for (int i = 0; i <=n; ++i) p = p+hs[i]*mu(x,n-i); return p; } } public class Tester { static void Main() { DaThuc p = new DaThuc(); p.nhap();
Console.WriteLine("Gia tri cua da thuc ={0}", DaThuc.can3(p.gtdt()/3)); } }
Như vậy phương thức tĩnh là phương thức chung cho tất cả các đối tượng của lớp,
nó không thuộc bất kỳ đối tượng nào. Ta có thể coi phương thức tĩnh như là một phương
thức toàn cục của lớp, các phương thức tình chỉ dùng để xử lý các thành phần tĩnh
Ví dụ trong phương thức can3 ta viết như sau là sai
public static double can3(double x) { double y; n=10;
/* Sai vi n không phải là thuộc tính tĩnh. Nếu viết như này thì chương trình dịch hiểu là:
this.n=10. Nhưng trong phương thức tĩnh thì không có đối ngầm định là this*/ if (x == 0) y = 0;
Đề cương Lập trình hướng đối tượng với C# Trang 69
else if (x > 0) y = Math.Exp(1.0 / 3 * Math.Log(x));
else y = -Math.Exp(1.0 / 3 * Math.Log(-x)); return x; }
4.2. Lớp bao và cách sử dụng
Những lớp lồng bên trong có lợi là có khả năng truy cập đến tất cả các thành viên của lớp ngoài.
Một phương thức của lớp lồng có thể truy cập đến biến thành viên private của lớp ngoài.
Hơn nữa, lớp lồng bên trong có thể ẩn đối với tất cả các lớp khác, lớp lồng có thể là private cho lớp ngoài.
Cuối cùng, một lớp làm lồng bên trong là public và được truy cập bên trong phạm
vi của lớp ngoài. Nếu một lớp Outer là lớp ngoài, và lớp Nested là lớp public lồng bên
trong lớp Outer, chúng ta có thể tham chiếu đến lớp Tested như Outer.Nested, khi đó lớp
bên ngoài hành động ít nhiều giống như một namespace hay một phạm vi. class Class1 { private int a,b; public Class1 (int a, int b) { this.a = a; this.b = b; } public class con { public void tong(Class1 l) {
System.Console.WriteLine("day la lop long ben trong ");
System.Console.WriteLine("a {0} + b {1} = {2}", l.a, l.b, l.a + l.b); } } }
Đề cương Lập trình hướng đối tượng với C# Trang 70
Điều thú vị trong phương thức Tong() truy cập dữ liệu thành viên private l.a
l.b. Hai viến thành viên private này sẽ không cho phép truy cập nếu con không phải là
lớp lồng bên trong của lớp Class1
Lưu ý là trong hàm Main() khi khai báo một thể hiện của lớp lồng bên trong, chúng
ta phải xác nhận tên của lớp bên ngoài, tức là lớp Class1. Class1 t1 = new Class1(4, 5);
Class1.con t2 = new Class1.con(); t2.tong(t1);
Đề cương Lập trình hướng đối tượng với C# Trang 71
Bài 5: Kế thừa và đa hình(1)
5.1. Giới thiệu chung về thừa kế
Thừa kế là một trong bốn nguyên tắc cơ sở của phương pháp lập trình hướng đối
tượng. Đặc biệt đây là cơ sở cho việc nâng cao khả năng sử dụng lại các bộ phận của
chương trình. Thừa kế cho phép ta định nghĩa một lớp mới, gọi là lớp dẫn xuất, từ một
lớp đã có, gọi là lớp cơ sở. Lớp dẫn xuất sẽ thừa kế các thành phần (dữ liệu, hàm) của lớp
cơ sở, đồng thời thêm vào các thành phần mới, bao hàm cả việc làm “tốt hơn” hoặc làm
lại những công việc mà trong lớp cơ sở chưa làm tốt hoặc không còn phù hợp với lớp dẫn
xuất. Chẳng hạn có thể định nghĩa lớp “mặt hàng nhập khẩu” dựa trên lớp “mặt hàng”,
bằng cách bổ sung thêm thuộc tính “thuế”. Khi đó cách tính chênh lệch giá bán, mua cũ
trong lớp “mặt hàng” sẽ không phù hợp nữa nên cần phải sửa lại cho phù hợp. Lớp điểm
có màu được định nghĩa dựa trên lớp điểm không màu bằng cách bổ sung thêm thuộc tính
màu, hàm display() lúc này ngoài việc hiển thị hai thành phần toạ độ còn phải cho biết
màu của đối tượng điểm. Trong cả hai ví dụ đưa ra, trong lớp dẫn xuất đều có sự bổ sung
và thay đổi thích hợp với tình hình mới.
Thừa kế cho phép không cần phải biên dịch lại các thành phần chương trình vốn đã
có trong các lớp cơ sở và hơn thế nữa không cần phải có chương trình nguồn tương ứng.
Kỹ thuật này cho phép chúng ta phát triển các công cụ mới dựa trên những gì đã có được.
Thừa kế cũng cho phép nhiều lớp có thể dẫn xuất từ cùng một lớp cơ sở, nhưng
không chỉ giới hạn ở một mức: một lớp dẫn xuất có thể là lớp cơ sở cho các lớp dẫn xuất
khác. ở đây ta thấy rằng khái niệm thừa kế giống như công cụ cho phép mô tả cụ thể hoá
các khái niệm theo nghĩa: lớp dẫn xuất là một cụ thể hoá hơn nữa của lớp cơ sở và nếu bỏ
đi các dị biệt trong các lớp dẫn xuất sẽ chỉ còn các đặc điểm chung nằm trong lớp cơ sở.
Hình 4.1 mô tả một sơ đồ thừa kế của các lớp, có cung đi từ lớp này sang lớp kia nếu
chúng có quan hệ thừa kế. Ta gọi đó là đồ thị thừa kế. Sau đây là một số mô tả cho các
lớp xuất hiện trong đồ thị thừa kế ở trên.
Đề cương Lập trình hướng đối tượng với C# Trang 72 1. Lớp mặt hàng các thuộc tính tên số lượng trong kho giá mua giá bán các phương thức
hàm chênh lệch giá bán mua {giá bán - giá mua} thủ tục mua(q)
{Thêm vào trong kho q đơn vị mặt hàng} thủ tục bán(q)
{Bớt đi q đơn vị mặt hàng có trong kho}
2. Lớp mặt hàng nhập khẩu thừa kế từ mặt hàng các thuộc tính thuế nhập khẩu các phương thức
hàm chênh lệch giá bán -mua
{giá bán - giá mua* thuế nhập khẩu}
3. Lớp xe gắn máy thừa kế từ mặt hàng nhập khẩu các thuộc tính dung tích xy lanh các phương thức
4. Lớp hàng điện tử dân dụng thừa kế từ mặt hàng các thuộc tính
Đề cương Lập trình hướng đối tượng với C# Trang 73 điện áp thời hạn bảo hành các phương thức
hàm thời gian bảo hành thực tế ...
Tính đa hình cũng là một trong các điểm lý thú trong lập trình hướng đối tượng, được
thiết lập trên cơ sở thừa kế trong đó đối tượng có thể có biểu hiện khác nhau tuỳ thuộc
vào tình huống cụ thể. Tính đa hình ấy có thể xảy ra ở một hành vi của đối tượng hay
trong toàn bộ đối tượng. Ví dụ trực quan thể hiện tính đa hình là một ti vi có thể vừa là
đối tượng của mặt hàng vừa là đối tượng của lớp mặt hàng điện tử dân dụng. Các đối
tượng hình học như hình vuông, hình tròn, hình chữ nhật đều có cùng cách vẽ như nhau:
xác định hai điểm đầu và cuối, nối hai điểm này. Do vậy thuật toán tuy giống nhau đối
với tất cả các đối tượng hình, nhưng cách vẽ thì phụ thuộc vào từng lớp đối tượng cụ thể.
Ta nói phương thức nối điểm của các đối tượng hình học có tính đa hình. Tính đa hình
còn được thể hiện trong cách thức hiển thị thông tin trong các đối tượng điểm màu/không màu.
Các ngôn ngữ lập trình hướng đối tượng đều cho phép đa thừa kế, theo đó một lớp có
thể là dẫn xuất của nhiều lớp khác. Do vậy dẫn tới khả năng một lớp cơ sở có thể được
thừa kế nhiều lần trong một lớp dẫn xuất khác, ta gọi đó là sự xung đột thừa kế. Điều này
hoàn toàn không hay, cần phải tránh. Từng ngôn ngữ sẽ có những giải pháp của riêng
mình, C# đưa ra khái niệm thừa kế ảo.
Trong chương này ta sẽ đề cập tới các khả năng của C# để thể hiện nguyên tắc thừa
kế khi viết chương trình.
Đề cương Lập trình hướng đối tượng với C# Trang 74 Lớp mặt hàng các thuộc tính tên số lượng trong kho giá mua giá bán các phương thức
chênh lệch giá bán mua mua(q) bán(q)
Lớp mặt hàng nhập khẩu các thuộc tính thuế nhập khẩu các phương thức
Lớp Đồ điện tử dân dụng
chênh lệch giá bán -mua các thuộc tính điện áp thời hạn bảo hành các phương thức Lớp Xe gắn máy
thời gian bảo hành thực tế các thuộc tính dung tích xy lanh Lớp Tivi các thuộc tính Lớp Ôtô
kích thước màn hình các thuộc tính
điều khiển từ xa Mác
5.2. Xây dựng lớp dẫn xuất thừa kế từ lớp cơ sở
Xây dựng lớp Luong để tính lương cho các cán bộ với các thông tin sau:
Các thành phần dữ liệu
- String Hoten; // Là thuộc tình dùng để nhập thông tin về họ tên cho Cấn bộ
- double HesSoLuong;// Là thuộc tính dùng để nhập thong tin về hệ số lương cho cán bộ
- int LuongCoBan;// Là thuộc tính dùng để nhập lương cơ bản cho các cán
bộ(Biết rằng tất cả các cán bộ thì có chung lương cơ bản Các phương thức
Đề cương Lập trình hướng đối tượng với C# Trang 75
- Phương thức khởi tạo không tham số dùng để khởi tạo giá trị lương cơ bản
LuongCoBan = 450, HeSoLuong=2.34;
- Phương thức thiết lập hai tham số dùng để khởi tạo LuongCoBan và
HesoLuong thông qua đối số của phương thức
- Phương thức nhập thông tin của lớp
- Phương thức hiện thông tin của lớp
- Phương thức tính lương theo công thức HeSoLuong*LuongCoBan
Sau đó kế thừa lớp Luong để xây dựng lớp LuongMoi dùng để tính lương mới cho
các cán bộ với việc bổ xung hệ số phụ cấp(HeSoPhuCap) cho mỗi cán bộ. Từ đó việc
tính lương được tính theo công thức:
(HeSoLuong*LuongCoBan)+(HeSoLuong*LuongCoBan)*HeSoPhuCap. Hãy bổ xung
các thành phần dữ liệu cũng như các phương thứ c cần thiết vào lớp LuongMoi để có thể
tính lương cho cán bộ theo chuẩn mới đã nêu.
Trong ngụn ngữ C# để tạo một lớp dẫn xuất từ một lớp ta thêm dấu hai chấm vào
sau tên lớp dẫn xuất và trước tên lớp cơ sở:
Xây dựng chương trình sử dụng các lớp trên
Ví dụ 12.1 Xây dựng lớp cơ sở và lớp kế thừa using System; class Luong { private string Hoten; private double HeSoLuong; public static int LuongCoBan; public Luong() { LuongCoBan = 450; HeSoLuong = 2.34; }
public Luong(int lcb,double hsl) {
Đề cương Lập trình hướng đối tượng với C# Trang 76 LuongCoBan = lcb; HeSoLuong = hsl; } public void Nhap() { Console.Write("Ho va ten:"); Hoten = Console.ReadLine();
Console.Write("He so luong:");
HeSoLuong = int.Parse(Console.ReadLine()); } public double TinhLuong() {
return LuongCoBan * HeSoLuong; } public void Hien() {
Console.WriteLine("Ho ten:{0}\nHe so luong:{1}", Hoten, HeSoLuong); } } class LuongMoi : Luong { private double HeSoPhuCap; public LuongMoi(): base() { HeSoPhuCap = 0.4; }
public LuongMoi(int lcb, double hsl, double hspc): base(lcb, hsl) { HeSoPhuCap = hspc; } public new void Nhap() { base.Nhap();
Console.Write("He so phu cap:");
Đề cương Lập trình hướng đối tượng với C# Trang 77
HeSoPhuCap = double.Parse(Console.ReadLine()); } public new double TinhLuong() {
return base.TinhLuong() + base.TinhLuong() * HeSoPhuCap; } } class Tester { static void Main() { LuongMoi a = new LuongMoi(); a.Nhap(); a.Hien();
Console.WriteLine("Luong:{0}", a.TinhLuong()); Console.ReadKey(); } }
5.3. Mức truy cập trong lớp dẫn xuất
Khả năng hiện hữu của một lớp và các thành viên của nó có thể được hạn chế
thông qua việc sử dụng các bổ sung truy cập: public, private, protected, internal, và protected internal.
Như chúng ta đã thấy, public cho phép một thành viên có thể được truy cập bởi
một phương thức thành viên của những lớp khác. Trong khi đó private chỉ cho phép các
phương thức thành viên trong lớp đó truy xuất. Từ khóa protected thì mở rộng thêm khả
năng của private cho phép truy xuất từ các lớp dẫn xuất của lớp đó. Internal mở rộng khả
năng cho phép bất cứ phương thức của lớp nào trong cùng một khối kết hợp (assembly)
có thể truy xuất được. Một khối kết hợp được hiểu như là một khối chia xẻ và dùng lại
trong CLR. Thông thường, khối này là tập hợp các tập tin vật lý được lưu trữ trong một
thư mục bao gồm các tập tin tài nguyên, chương trình thực thi theo ngôn ngữ IL,...
Từ khóa internal protected đi cùng với nhau cho phép các thành viên của cùng một
khối assembly hoặc các lớp dẫn xuất của nó có thể truy cập.
Đề cương Lập trình hướng đối tượng với C# Trang 78
Chúng ta có thể xem sự thiết kế này giống như là internal hay protected. Các lớp
cũng như những thành viên của lớp có thể được thiết kế với bất cứ mức độ truy xuất nào.
Một lớp thường có mức độ truy xuất mở rộng hơn cách thành viên của lớp, còn các thành
viên thì mức độ truy xuất thường có nhiều hạn chế. Do đó, ta có thể định nghĩa một lớp MyClass như sau: public class MyClass { //... protected int myValue; }
Như trên biến thành viên myValue được khai báo truy xuất protected mặc dù bản
thân lớp được khai báo là public. Một lớp public là một lớp sẵn sàng cho bất cứ lớp nào
khác muốn tương tác với nó. Đôi khi một lớp được tạo ra chỉ để trợ giúp cho những lớp
khác trong một khối assemply, khi đó những lớp này nên được khai báo khóa internal hơn là khóa public.
Dưới đây là bảng về mức truy cập các thành phần của một lớp
Các thành phần trong
Lớp C không kế thừa lớp Lớp A
Lớp B kế thừa lớp A A private Không truy xuất được Không truy xuất được protected
Truy xuất được vì B dẫn xuất từ A Không truy xuất được internal
Truy xuất được nếu B(C) cùng khối kết hợp (assembly) với A (cùng một project) protected internal
Truy xuất được vì B dẫn xuất từ A
Truy xuất được nếu C cùng
khối kết hợp (assembly) với A
5.4. Gọi phương thức khởi tạo của lớp cơ sở
Trong ví trên một lớp mới tên là LuongMoi được dẫn xuất từ lớp cơ sở Luong, lớp
LuongMoi có hai phương thức khởi tạo một phương thức khởi tạo không tham số và một
Đề cương Lập trình hướng đối tượng với C# Trang 79
phương thức khởi tạo ba tham số. Trong hai phương thức khởi tạo của lớp dẫn xuất này
có gọi phương thức khởi tạo của lớp cơ sở. Cách gọi được thực hiện bằng việc đặt dấu
hai chấm ngay sau phần khai báo danh sách tham số và tham chiếu đến lớp cơ sở thông qua từ khóa base: public LuongMoi(): base() { HeSoPhuCap = 0.4; }
public LuongMoi(int lcb, double hsl, double hspc): base(lcb, hsl) { HeSoPhuCap = hspc; }
Bởi vì các lớp không được kế thừa các phương thức khởi dựng của lớp cơ sở, do đó lớp
dẫn xuất phải thực thi phương thức khởi dựng riêng của nó. Và chỉ có thể sử dụng
phương thức khởi dựng của lớp cơ sở thông qua việc gọi tường minh.
Một điều lưu ý trong ví dụ trên là việc lớp LuongMoi thực thi một phiên bản mới của
phương thức Nhap() và TinhLuong(): public new void Nhap()
và public new double TinhLuong()
Từ khóa new được sử dụng ở đây để chỉ ra rằng người lập trình đang tạo ra một phiên
bản mới cho phương thức này bên trong lớp dẫn xuất. Nếu lớp cơ sở có phương thức
khởi dựng mặc định, thì lớp dẫn xuất không cần bắt buộc phải gọi phương thức khởi
dựng của lớp cơ sở một cách tường minh. Thay vào đó phương thức khởi dựng mặc định
của lớp cơ sở sẽ được gọi một cách ngầm định. Tuy nhiên, nếu lớp cơ sở không có
phương thức khởi dựng mặc định, thì tất cả các lớp dẫn xuất của nó phải gọi phương thức
khởi dựng của lớp cơ sở một cách tường minh thông qua việc sử dụng từ khóa base.
5.5. Truy xuất các thành phần của lớp cơ sở
Trong ví dụ trên phương thức Nhap() của lớp LuongMoi sẽ làm ẩn và thay thế
phương thức Nhap của lớp cơ sở Luong. Khi chúng ta gọi phương thức Nhap của một đối
tượng của lớp LuongMoi thì phương thức LuongMoi.Nhap() sẽ được thực hiện, không
Đề cương Lập trình hướng đối tượng với C# Trang 80
phải phương thức Luong.Nhap() của lớp cơ sở Luong. Tuy nhiên, ta có thể gọi phương
thức Nhap() của lớp cơ sở thông qua từ khóa base:
base.Nhap(); // gọi phương thức cơ sở
Từ khóa base chỉ đến lớp cơ sở cho đối tượng hiện hành
5.6. Boxing và Unboxing dữ liệu
Boxing và unboxing là những xử lý cho phép kiểu dữ liệu giá trị (như int,
long,...) được đối xử như kiểu dữ liệu tham chiếu (các đối tượng). Một giá trị được
đưa vào bên trong của đối tượng, được gọi là Boxing. Trường hợp ngược lại,
Unboxing sẽ chuyển từ đối tượng ra một giá trị. Xử lý này đã cho phép chúng ta gọi
phương thức ToString( ) trên kiểu dữ liệu int trong ví dụ 3.4.
Boxing được thực hiện ngầm định
Boxing là một sự chuyển đổi ngầm định của một kiểu dữ liệu giá trị sang kiểu
dữ liệu tham chiếu là đối tượng. Boxing một giá trị bằng cách tạo ra một thể hiển của
đối tượng cần dùng và sao chép giá trị trên vào đối tượng mới tạo. Ta có hình vẽ
sau minh họa quá trình Boxing một số nguyên. Boxing số nguyên.
Boxing được thực hiện ngầm định khi chúng ta đặt một kiểu giá trị vào một tham
chiếu đang chờ đợi và giá trị sẽ được đưa vào đối tượng một cách tự động ngầm định.
Ví dụ, nếu chúng ta gán một kiểu dư liệu cơ bản như kiểu nguyên int vào một biến
Đề cương Lập trình hướng đối tượng với C# Trang 81
kiểu Object (điều này hoàn toàn hợp lệ vì kiểu int được dẫn xuất từ lớp Object) thì
giá trị này sẽ được đưa vào biến Object, như minh họa sau: using System; class Boxing { public static void Main() { int i = 123;
Console.WriteLine(“The object value = {0}”, i); } }
Unboxing phải được thực hiện tường minh
Việc đưa một giá trị vào một đối tượng được thực hiện một cách ngầm định. Và
sự thực hiện ngược lại, unboxing, tức là đưa từ một đối tượng ra một giá trị phải được
thực hiện một cách tường minh. Chúng ta phải thiết lập theo hai bước sau:
Phải chắc chắn rằng đối tượng đã boxing đúng kiểu giá trị đưa ra.
Sao chép giá trị từ thể hiện hay đối tượng vào biến kịểu giá trị.
Unboxing sau khi thực hiện Boxing.
Để thực hiện unboxing thành công, thì đối tượng được unboxing phải được tham
chiếu đến một đối tượng, và đối tượng này đã được tạo ra bằng việc boxing một giá
Đề cương Lập trình hướng đối tượng với C# Trang 82
trị cùng với kiểu của giá trị được đưa ra. Boxing và Unboxing được minh họa trong ví dụ sau
Ví dụ : Boxing và Unboxing. using System; public class UnboxingTest { public static void Main() { int i = 123; // Boxing object o = i;
// Unboxing phải được tường minh int k = (int) o;
Console.WriteLine(“k: {0}”, k); } }
Ví dụ trên tạo một số nguyên i và thực hiện boxing ngầm định khi i được gán cho
một đối tượng o. Sau đó giá trị được unboxing một cách tường minh và gán đến một
biến nguyên int mới, và cuối cùng giá trị được hiển thị.
Thông thường, chúng ta sẽ bao bọc các hoạt động unboxing trong khối try. Nếu một đối
tượng được Unboxing là null hay là tham chiếu đến một đối tượng có kiểu dữ liệu khác,
một InvalidCastException sẽ được phát sinh.
5.7. Các lớp lồng nhau
Các lớp chứa những thành viên, và những thành viên này có thể là một lớp khác
có kiểu do người dùng định nghĩa (user-defined type). Do vậy, một lớp Button có thể
có một thành viên của kiểu Location, và kiểu Location này chứa thành viên của kiểu
dữ liệu Point. Cuối cùng, Point có thể chứa chứa thành viên của kiểu int.
Cho đến lúc này, các lớp được tạo ra chỉ để dùng cho các lớp bên ngoài, và chức
năng của các lớp đó như là lớp trợ giúp (helper class). Chúng ta có thể định nghĩa một
lớp trợ giúp bên trong các lớp ngoài (outer class). Các lớp được định nghĩa bên
trong gọi là các lớp lồng (nested class), và lớp chứa được gọi đơn giản là lớp ngoài.
Đề cương Lập trình hướng đối tượng với C# Trang 83
Những lớp lồng bên trong có lợi là có khả năng truy cập đến tất cả các thành viên
của lớp ngoài. Một phương thức của lớp lồng có thể truy cập đến biến thành viên
private của lớp ngoài. Hơn nữa, lớp lồng bên trong có thể ẩn đối với tất cả các lớp
khác, lớp lồng có thể là private cho lớp ngoài.
Cuối cùng, một lớp làm lồng bên trong là public và được truy cập bên trong phạm vi
của lớp ngoài. Nếu một lớp Outer là lớp ngoài, và lớp Nested là lớp public lồng bên
trong lớp Outer, chúng ta có thể tham chiếu đến lớp Tested như Outer.Nested, khi
đó lớp bên ngoài hành động ít nhiều giống như một namespace hay một phạm vi.
Ghi chú: Đối với người lập trình Java, lớp lồng nhau trong C# thì giống như những
lớp nội static (static inner) trong Java. Không có sự tương ứng trong C# với
những lớp nội nonstatic (nonstatic inner) trong Java.
Ví dụ 3.4 sau sẽ thêm một lớp lồng vào lớp Fraction tên là FractionArtist. Chức năng
của lớp FractionArtis là vẽ một phân số ra màn hình. Trong ví dụ này, việc vẽ sẽ được
thay thế bằng sử dụng hàm WriteLine xuất ra màn hình console.
Ví dụ: Sử dụng lớp lồng nhau. using System; using System.Text; public class Fraction {
public Fraction( int numerator, int denominator) { this.numerator = numerator;
this.denominator = denominator; }
public override string ToString() {
StringBuilder s = new StringBuilder();
s.AppendFormat(“{0}/{1}”,numerator, denominator); return s.ToString(); } internal class FractionArtist {
Đề cương Lập trình hướng đối tượng với C# Trang 84 public void Draw( Fraction f) {
Console.WriteLine(“Drawing the numerator {0}”, f.numerator);
Console.WriteLine(“Drawing the denominator {0}”, f.denominator); } }
// biến thành viên private private int numerator; private int denominator; } public class Tester { static void Main() {
Fraction f1 = new Fraction( 3, 4);
Console.WriteLine(“f1: {0}”, f1.ToString());
Fraction.FractionArtist fa = new Fraction.FractionArtist(); fa.Draw( f1 ); } }
Lớp Fraction trên nói chung là không có gì thay đổi ngoại trừ việc thêm một lớp
lồng bên trong và lược đi một số phương thức không thích hợp trong ví dụ này. Lớp
lồng bên trong FractionArtist chỉ cung cấp một phương thức thành viên duy nhất,
phương thức Draw(). Điều thú vị trong phương thức Draw() truy cập dữ liệu thành
viên private là f.numerator và f.denominator. Hai viến thành viên private này
sẽ không cho phép truy cập nếu FractionArtist không phải là lớp lồng bên trong của lớp Fraction.
Lưu ý là trong hàm Main() khi khai báo một thể hiện của lớp lồng bên trong, chúng
ta phải xác nhận tên của lớp bên ngoài, tức là lớp Fraction:
Fraction.FractionArtist fa = new Fraction.FractionArtist();
Thậm chí khi lớp FractionArtist là public, thì phạm vị của lớp này vẫn nằm bên trong của lớp Fraction.
Đề cương Lập trình hướng đối tượng với C# Trang 85
Bài 6: Kế thừa và đa hình(2)
6.1. Giới thiệu chung về đa hình
Có hai cách thức để thực hiện việc kế thừa. Một là sử dụng lại mã nguồn, khi
chúng ta tạo ra lớp HinhVuong, chúng ta có thể sử dụng lại một vài các thành phần
trong lớp cơ sở như HinhChuNhat.
Tuy nhiên, cách sử dụng thứ hai chứng tỏ được sức mạnh to lớn của việc kế thừa
đó là tính đa hình (polymorphism). Theo tiếng Anh từ này được kết hợp từ poly là
nhiều và morph có nghĩa là form (hình thức). Do vậy, đa hình được hiểu như là khả
năng sử dụng nhiều hình thức của một kiểu mà không cần phải quan tâm đến từng chi tiết.
Khi một tổng đài điện thoại gởi cho máy điện thoại của chúng ta một tín hiệu có
cuộc gọi. Tổng đài không quan tâm đến điện thoại của ta là loại nào. Có thể ta đang
dùng một điện thoại cũ dùng motor để rung chuông, hay là một điện thoại điện tử
phát ra tiếng nhạc số. Hoàn toàn các thông tin về điện thoại của ta không có ý nghĩa
gì với tổng đài, tổng đài chỉ biết một kiểu cơ bản là điện thoại mà thôi và diện thoại
này sẽ biết cách báo chuông. Còn việc báo chuông như thế nào thì tổng đài không quan
tâm. Tóm lại, tổng đài chỉ cần bảo điện thoại hãy làm điều gì đó để reng. Còn phần còn
lại tức là cách thức reng là tùy thuộc vào từng loại điện thoại. Đây chính là tính đa hình.
Tính đa hình chính là cách thực hiện cùng một cách thức với nhiều loại đối tượng khác nhau.
6.2. Phương thức đa hình
Để xây dựng một phương thức hỗ trợ tính đa hình, ta đặt từ khóa
virtual trong phương thức của lớp cơ sở. Ví dụ, để chỉ định rằng phương thức
Hien Thi () của lớp DongVat trong ví dụ dưới đây là đa hình, đơn giản là ta thêm từ
khóa virtual vào khai báo trong phương thức như sau: public virtual void HienThi() {
Console.WriteLine("con {0} co {1} chan ,song o moi truong {2}", ten,
Đề cương Lập trình hướng đối tượng với C# Trang 86 chan, moitruong); }
Lúc này thì các lớp dẫn xuất được tự do thực thi các cách xử lý riêng của
mình trong phiên bản mới của phương thức HienThi(). Để làm được điều này chỉ
cần thêm từ khóa override để chồng lên phương thức ảo HienThi () của lớp cơ sở.
Sau đó thêm các đoạn mã nguồn mới vào phương thức viết chồng này.
Trong ví dụ minh họa sau, lớp ConMeo dẫn xụất từ lớp DongVat và thực thi một phiên
bản riêng của phương thức HienThi():
public override void HienThi() { base.HienThi();
Console.WriteLine("con vat ma toi yeu thich la con {0} co {1}
chan,thich an {2}", ten, chan, moitruong); }
Từ khóa override bảo với trình biên dịch rằng lớp này thực hiện việc phủ quyết lại
phương thức HienThi() của lớp cơ sở. Tương tự như vậy ta có thể thực hiện việc
phủ quyết phương thức này trong một lớp dẫn xuất khác mà được dẫn xuất từ lớp Do ng Va t
Ví dụ: Xây dựng phương thức ảo trong lớp cơ sở, và ghi đè phương thức ảo trong
lớp kế thừa using System;
using System.Collections.Generic; using System.Text; namespace vidu { class Program {
static void Main(string[] args) {
Đề cương Lập trình hướng đối tượng với C# Trang 87
DongVat dongVat1 = new DongVat("ga", 2, "can");
ConMeo conmeo1 = new ConMeo("meo", 4, "can");
Console.WriteLine("ket qua cua lop dong vat"); dongVat1.HienThi();
Console.WriteLine("ket qua cua lop con meo "); conmeo1.HienThi(); Console.ReadLine(); } } public class DongVat { protected string ten; protected int chan; protected string moitruong;
public DongVat(string ten, int chan, string moitruong) { this.ten = ten; this.chan = chan; this.moitruong = moitruong; }
//phuong thuc duoc khai bao la ao public virtual void HienThi() {
Console.WriteLine("con {0} co {1} chan ,song o moi truong {2}", ten, chan, moitruong); } } public class ConMeo : DongVat {
public ConMeo(string ten, int chan, string moitruong) : base(ten, chan, moitruong) { }
Đề cương Lập trình hướng đối tượng với C# Trang 88
//phu quyet phuong thuc HienThi cua lop co so
public override void HienThi() { base.HienThi();
Console.WriteLine("con vat ma toi yeu thich la con {0} co {1}
chan,thich an {2}", ten, chan, moitruong); } }
Đoạn chương trình trên chúng ta thấy tính đa hình chưa được thể hiện trong chương
trình Main. Tính đa hình sẽ được thể hiện rõ trong trường hợp như sau:
DongVat[] dv = new DongVat[2];
dv[0] = new DongVat("Cho", 4, "Vat nuoi can");
dv[1] = new ConMeo("Meo muop", 4, "Vat nuoi can"); dv[0].HienThi(); dv[1].HienThi();
Khi thực hiện đoạn chương trình trên trình biên dịch biết rằng mảng có hai đối
tượng DongVat và phải thực hiện thực thi phương thức HienThi cho các đối tượng này.
Nếu chúng ta không đánh dấu phương thức HienThi trong lớp DongVat là virtual thì
phương thức HienThi của lớp DongVat sẽ được gọi 2 lần. Tuy nhiên do chúng ta đã
đánh dấu phương thức HienThi ở lớp cơ sở là phương thức ảo và thực thi phủ quyết ở lớp dẫn xuất
Khi ta gọi phương thức HienThi trong mảng, trình biên dịch sẽ dò ra được chính
xác kiểu dữ liệu nào được thực thi trong mảng khi đó có hai kiểu sẽ được thực thi là
một DongVat, một ConMeo. Và trình biên dịch sẽ gọi chính xác phương thức của từng đối tượng.
Ta thấy rằng mảng dv được khai báo là đối tượng lớp DongVat. Nhưng khi nhận giá trị
thì dv[0] được gán cho một đối tượng lớp DongVat, dv[1] được gán cho một đối tượng
lớp ConMeo. Như vậy phương thức dv[0].HienThi() thì sẽ thực thi phương thức
HienThi của lớp DongVat, còng dv[1].HienThi() thì sẽ thực thi phương thức HienThi
của lớp ConMeo. Ta thấy dv[0] và dv[1] là hai loại khác nhau nhưng lại có cùng một
Đề cương Lập trình hướng đối tượng với C# Trang 89
cách thức thực hiện phương thức HienThi() là như nhau đây chính là thể hiện tính đa hình.
Lưu ý: Nếu phương thức của lớp cơ sở là phương thức ảo, ta có thể phủ quyết lớp ảo ở
lớp dẫn xuất băng từ khóa override, hoặc không phủ quyết phương thức ảo, khi đó
phương thức ảo ở lớp cơ sở trở thành phương thức ảo ở lớp dẫn xuất.
6.3. Từ khoá new và override
Từ khóa new và override
Trong ngôn ngữ C#, người lập trình có thể quyết định phủ quyết một phương
thức ảo bằng cách khai báo tường minh từ khóa override. Điều này giúp cho ta đưa ra
một phiên bản mới của chương trình và sự thay đổi của lớp cơ sở sẽ không làm ảnh
hưởng đến chương trình viết trong các lớp dẫn xuất. Việc yêu cầu sử dụng từ khóa
override sẽ giúp ta ngăn ngừa vấn đề này.
Bây giờ ta thử bàn về vấn đề này, giả sử lớp cơ sở DongVat của ví dụ trước được
viết bởi một công ty A. Cũng giả sử rằng lớp ConMeo đươc viết từ những người lập
trình của công ty B và họ dùng lớp cơ sở DongVat mua được của công ty A làm lớp cơ
sở cho lớp trên. Người lập trình trong công ty B không có hoặc có rất ít sự kiểm soát
về những thay đổi trong tương lai với lớp DongVat do công ty A phát triển.
Khi nhóm lập trình của công ty B quyết định thêm một phương thức Keu( ) vào lớp ConMeo:
public virtual void Keu(string tiengkeu) {
Console.WriteLine("Con {0} keu {1}", ten, tiengkeu); }
Việc thêm vào vẫn bình thường cho đến khi công ty A, tác giả của lớp cơ sở DongVat,
đưa ra phiên bản thứ hai của lớp DongVat. Và trong phiên bản mới này những người
lập trình của công ty A đã thêm một phương thức Keu( ) vào lớp cơ sở DongVat: public class Window {
Đề cương Lập trình hướng đối tượng với C# Trang 90 //……..
public virtual void Keu(string tiengkeu) {//……………. } }
Trong các ngôn ngữ lập trình hướng đối tượng khác như C++, phương thức ảo mới
Keu() trong lớp Do ng Vat bây giờ sẽ hành động giống như là một phương thức cơ
sở cho phương thức ảo trong lớp ConMeo. Trình biên dịch có thể gọi phương thức
Keu( ) trong lớp ConMeo khi chúng ta có ý định gọi phương thức Keu( ) trong
Do ng Vat. Trong ngôn ngữ Java, nếu phương thức Keu( ) trong DongVat có kiểu
trả về khác kiểu trả về của phương thức Keu( ) trong lớp ConMeo thì sẽ được báo lỗi
là phương thức phủ quyết không hợp lệ.
Ngôn ngữ C# ngăn ngừa sự lẫn lộn này, trong C# một phương thức ảo thì được xem
như là gốc rễ của sự phân phối ảo. Do vậy, một khi C# tìm thấy một phương thức khai
báo là ảo thì nó sẽ không thực hiện bất cứ việc tìm kiếm nào trên cây phân cấp kế
thừa. Nếu một phương thức ảo Keu( ) được trình bày trong lớp DongVat, thì khi thực
hiện hành vi của lớp ConMeo không thay đổi.
Tuy nhiên khi biên dịch lại, thì trình biên dịch sẽ đưa ra một cảnh báo giống như sau:
…. 'LTHDTC.ConMeo.Keu(string)' hides inherited member
'LTHDTC.DongVat.Keu(string)'. To make the current member override that
implementation, add the override keyword. Otherwise add the new keyword.
C:\Tai lieu giang day\Lap trinh huong doi tuong\Vi du\LTHDTC\LTHDTC\DongVat.cs 53 25 LTHDTC
Để loại bỏ cảnh báo này, người lập trình phải chỉ rõ ý định của anh ta. Anh ta có thể
đánh dấu phương thức ConMeo.Keu( ) với từ khóa là new, và nó không phải phủ
quyết của bất cứ phương thức ảo nào trong lớp DongVat: public class ConMeo : DongVat {
public new virtual Keu( ) {….} }
Đề cương Lập trình hướng đối tượng với C# Trang 91
Việc thực hiện khai báo trên sẽ loại bỏ được cảnh báo. Mặc khác nếu người lập trình
muốn phủ quyết một phương thức trong DongVat, thì anh ta cần thiết phải dùng từ
khóa override để khai báo một cách tường minh: Public class ConMeo:DongVat { //……
Public override Keu(){.............. } }
Đề cương Lập trình hướng đối tượng với C# Trang 92
Bài 7: Kế thừa và đa hình(3)
7.1 Lớp trừu tượng
7.1.1. Xây dựng lớp cơ sở trừu tượng
Ta có một lớp Hinh và các lớp HinhChuNhat, HinhTron, HinhVuong kế thừa từ lớp
Hinh, lớp hình có các phương thức tính chu vi, tính diện tích, Vẽ. Nhưng với mỗi một
loại hình thì có cách tính diện tích, chu vi, vẽ là khác nhau. Nghĩa là với các phương
thức trên thì mỗi một lớp kế thừa từ lớp Hinh đòi hỏi phải thực thi một phương thức
cho riêng mình. Tuy nhiên nếu như chúng ta khai báo các phương thức trên trong lớp
Hinh là phương thức ảo thì điều này không thực sự đòi hỏi các lớp kế thừa phải ghi đè
các phương thức trên một cách bắt buộc. Để yêu cầu các lớp dẫn xuất bắt buộc phải
thực thi một phương thức của lớp cơ sở thì phương thức đó chúng ta phải khai báo là
phương thức trừu tượng.
Một phương thức trừu tượng không có sự thực thi. Phương thức này chỉ đơn giản
tạo ra một tên phương thức và ký hiệu của phương thức, phương thức này sẽ được thực
thi ở các lớp dẫn xuất. Một lớp mà chứa ít nhất một phương thức trừu tượng thì lớp đó
phải là lớp trừu tượng.
Những lớp trừu tượng được thiết lập như là cơ sở cho những lớp dẫn xuất, nhưng
việc tạo các thể hiện hay các đối tượng cho các lớp trừu tượng được xem là không
hợp lệ. Một khi chúng ta khai báo một phương thức là trừu tượng, thì chúng ta phải
ngăn cấm bất cứ việc tạo thể hiện cho lớp này.
Do vậy, nếu chúng ta thiết kế phương thức Chu Vi () như là trừu tượng trong
lớp Hinh, chúng ta có thể dẫn xuất từ lớp này, nhưng ta không thể tạo bất cứ đối tượng
cho lớp này. Khi đó mỗi lớp dẫn xuất phải thực thi phương thức ChuVi(). Nếu lớp
dẫn xuất không thực thi phương thức trừu tượng của lớp cơ sở thì lớp dẫn xuất đó
cũng là lớp trừu tượng, và ta cũng không thể tạo các thể hiện của lớp này được.
Phương thức trừu tượng được thiết lập bằng cách thêm từ khóa abstract vào đầu của
phần định nghĩa phương thức, cú pháp thực hiện như sau:
abstract public double ChuVi();
Do phương thức không cần phần thực thi, nên không có dấu ({}) mà chỉ có dấu chấm
Đề cương Lập trình hướng đối tượng với C# Trang 93
phẩy (;) sau phương thức. Như thế với phương thức ChuVi () được thiết kế là trừu
tượng thì chỉ cần câu lệnh trên là đủ.
Nếu một hay nhiều phương thức được khai báo là trừu tượng, thì phần định nghĩa
lớp phải được khai báo là abstract, với lớp Hinh ta có thể khai báo là lớp trừu tượng như sau: abstract class Hinh { //…….. }
Ví dụ : Phương thức trừu tượng class Diem { private int x; private int y; public Diem(int x, int y) { this.x = x; this.y = y; } public int X { set { x = value; } get { return x; } }
Đề cương Lập trình hướng đối tượng với C# Trang 94 public int Y { get { return y; } set { y = value; } } public void Draw() {
System.Console.WriteLine("Toa do x {0}, y {1}", x, y); }
public double KhoangCach(Diem d) {
return Math.Sqrt(Math.Pow((x - d.x), 2) + Math.Pow((y - d.y), 2)); }
public double KhoangCach(int x, int y) {
return Math.Sqrt(Math.Pow((x - this.x), 2) + Math.Pow((y - this.y), 2)); } }
// Vì lớp có phương thức trừu tượng, nên lớp cũng phải khai báo là lớp trừu tượng abstract class Hinh {
// Phương thức trừu tượng abstract public void Draw();
abstract public double DienTich();
abstract public double ChuVi(); public void thu() {
System.Console.WriteLine("chao tat ca nhung ai dang su dung lop");
Đề cương Lập trình hướng đối tượng với C# Trang 95 } } }
7.1.2. Kế thừa từ lớp trừu tượng
Khi một lớp được dẫn xuất từ một lớp trừu tượng thì lớp đó được kế thừa tất cả
các thành phần không phải là private của lớp cơ sở. Trong đó có cả các thành phần là trừu
tượng và không trừu tượng, khi đó lớp dẫn xuất bắt buộc phải ghi đè tất cả các phương
thức trừu tượng bằng từ khóa override. Nếu lớp dẫn xuất không ghi đè hết các phương
thức trừu tượng thì lớp dẫn xuất sẽ là lớp trừu tượng.
Ví dụ : Xây dựng lớp kế thừa từ lớp trừu tượng. class HinhTron : Hinh { private Diem tam; private double bankinh;
public HinhTron(Diem tam, double bankinh) { this.tam = tam; this.bankinh = bankinh; }
public HinhTron(int x, int y, double bankinh) { tam = new Diem(x, y); this.bankinh = bankinh; }
// Thực thi phương thức trừu tượng trong lớp cơ sở public override void Draw() {
System.Console.Write("Duong tron co:\n Toa do tam"); tam.Draw();
System.Console.WriteLine("Ban kinh: {0}", bankinh); }
// Thực thi phương thức trừu tượng trong lớp cơ sở
Đề cương Lập trình hướng đối tượng với C# Trang 96
public override double ChuVi() { return 2 * Math.PI * bankinh; }
// Thực thi phương thức trừu tượng trong lớp cơ sở
public override double DienTich() {
return Math.PI * bankinh * bankinh; } } class HinhChuNhat : Hinh { protected double a, b;
public HinhChuNhat(double a, double b) {
if ((a <= 0) | (b <= 0)) {
throw new ArithmeticException("Canh cua hinh chu nhat phai lon hon 0"); } else { this.a = a; this.b = b; } } public override void Draw() {
System.Console.WriteLine("Hinh chu nhat co hai canh \na= {0}\nb= {1}", a, b); }
public override double ChuVi()
Đề cương Lập trình hướng đối tượng với C# Trang 97 { return (a + b) * 2; }
public override double DienTich() { return (a * b); } } class HinhVuong : HinhChuNhat { public HinhVuong(double a) : base(a, a) { } public override void Draw() {
System.Console.WriteLine("Hinh vuong co canh la {0}", a); base.Draw(); } }
Khi đó ta có thể sử dụng các lớp trên để khai báo như sau : public static void Main() { Hinh h ;
HinhTron ht = new HinhTron(5, 7, 16);
HinhChuNhat hcn = new HinhChuNhat(5, 8);
HinhVuong hv = new HinhVuong(8); h = ht;
System.Console.WriteLine(h.ChuVi()); h = hcn;
System.Console.WriteLine(h.ChuVi()); h = hv;
System.Console.WriteLine(h.ChuVi()); }
Đề cương Lập trình hướng đối tượng với C# Trang 98
Khi đó đoạn chương trình trên hoàn toàn chạy được không gặp bất cứ một lỗi nào cả.
Nhưng nếu ta thay câu lệnh Hinh h bằng câu lệnh Hinh h = new Hinh() ; thì trình biên
dịch sẽ báo lỗi như sau :
Cannot create an instance of the abstract class or interface Hinh ……….
Quay trở lại tính đa hình mà chúng ta đã bàn trước đây, chúng ta muốn các loại đối tượng
khác nhau khi thực hiện một hành động cùng một cách thức với nhau. Như trong chương
trình Main trên thì biến đối tượng Hinh h được gán cho đối tượng HinhTron,
HinhChuNhat, HinhVuong, như vậy khi gọi các phương thức thì trình biên dịch sẽ kiểm
tra xem đối tượng h là đối tượng nào để gọi các phương thức tương ứng.
Sau đây là một ví dụ mà tính đa hình được thể hiện rất rõ. public static void Main() { Hinh h;
HinhTron ht = new HinhTron(5, 7, 16);
HinhChuNhat hcn = new HinhChuNhat(5, 8);
HinhVuong hv = new HinhVuong(8); ThaoTacHinh(ht); ThaoTacHinh(hv);
ThaoTacHinh(new HinhChuNhat(5, 8));
ThaoTacHinh(new HinhTron(5, 6, 7)); }
public static void ThaoTacHinh(Hinh h) {
System.Console.WriteLine(h.ToString()); h.Draw();
System.Console.WriteLine("Chu vi: {0}", h.ChuVi());
System.Console.WriteLine("Dien tich: " + h.DienTich()); }
Như vậy là thay vì phải xây dựng các phương thức khác nhau cho các kiểu đối tượng
hình khác nhau, ta có thể xây dựng một phương thức mà đối số truyền vào là lớp cha của
các lớp đó. Như vậy khi gọi thì ta có thể truyền vào các biến đối tượng mà kế thừa từ lớp
cha đó. Như vậy chúng ta thấy rõ ràng rằng dù là đối tượng hình gì thì khi muốn thực
Đề cương Lập trình hướng đối tượng với C# Trang 99
hiện các thao tác ta đều có một cách thức chung là gọi phương thức ThaoTacHinh và
truyền vào đối tượng Hình cần thực hiện. Đó chính là tính đa hình.
7.1.3. Hạn chế của lớp trừu tượng
Mặc dù chúng ta đã thiết kế phương thức ChuVi() như một phương thức trừu
tượng để hỗ trợ cho tất cả các lớp dẫn xuất được thực thi riêng, nhưng điều này có
một số hạn chế. Nếu chúng ta dẫn xuất một lớp từ lớp HinhChuNhat như lớp
HinhVuong, thì lớp này không được hỗ trợ để thực thi phương thức ChuVi( ) cho riêng nó.
Ghi chú: Khác với ngôn ngữ C++, trong C# phương thức Hinh.ChuVi( ) không thể
cung cấp một sự thực thi, do đó chúng ta sẽ không thể lấy được lợi ích của phương
thức ChuVi() bình thường dùng để chia xẻ bởi các lớp dẫn xuất.
Cuối cùng những lớp trừu tượng không có sự thực thi căn bản; chúng thể hiện ý tưởng
về một sự trừu tượng, điều này thiết lập một sự giao ước cho tất cả các lớp dẫn xuất.
Nói cách khác các lớp trừu tượng mô tả một phương thức chung của tất cả các lớp
được thực thi một cách trừu tượng.
Ý tưởng của lớp trừu tượng Hinh thể hiện những thuộc tính chung cùng với những
hành vi của tất cả các Hình, thậm chí ngay cả khi ta không có ý định tạo thể hiện
của chính lớp trừu tượng Hinh.
Ý nghĩa của một lớp trừu tượng được bao hàm trong chính từ “trừu tượng”. Lớp này
dùng để thực thi một “Hinh” trừu tượng, và nó sẽ được biểu lộ trong các thể hiện
xác định của Hinh, như là HinhTron, HinhVuong, HinhChuNhat…
Các lớp trừu tượng không thể thực thi được, chỉ có những lớp xác thực tức là những
lớp dẫn xuất từ lớp trừu tượng này mới có thể thực thi hay tạo thể hiện. Một sự thay
đổi việc sử dụng trừu tượng là định nghĩa một giao diện (interface), phần này sẽ được
trình bày trong Chương tiếp theo Một số chú ý:
❑ Không thể tạo đối tượng của lớp trừu tượng
❑ Một lớp chứa một phương thức trừu tượng thì lớp đó phải là lớp trừu tượng
Đề cương Lập trình hướng đối tượng với C# Trang 100
❑ Lớp dẫn xuất không hiện thực tất cả các phương thức trừu tượng của lớp
cơ sở thì lớp đó cũng phải là lớp trừu tượng
❑ Để khai báo phương thức trừu tượng ta sử dụng từ khoá abstract
❑ Phương thức trừu tượng không có phần thân
❑ Phương thức trừu tượng không thể là private
❑ Phương thức trừu tượng mặc định cũng là phương thức ảo
❑ Phương thức tĩnh không thể là phương thức trừu tượng
❑ Lớp dẫn xuất kề thừa phương thức trừu tượng thì phải ghi đè nó.
❑ Phương thức trừu tượng có thể ghi đè phương thức của lớp cơ sở được
khai báo là phương thức ảo
❑ Phương thức trừu tượng có thể ghi đè phương thức của lớp cơ sở đã đã được ghi đè
7.2. Lớp cô lập (sealed class)
Ngược với các lớp trừu tượng là các lớp cô lập. Một lớp trừu tượng được thiết kế
cho các lớp dẫn xuất và cung cấp các khuôn mẫu cho các lớp con theo sau. Trong khi
một lớp cô lập thì không cho phép các lớp dẫn xuất từ nó. Để khai báo một lớp cô
lập ta dùng từ khóa sealed đặt trước khai báo của lớp không cho phép dẫn xuất.
Hầu hết các lớp thường được đánh dấu sealed nhằm ngăn chặn các tai nạn do sự kế
Đề cương Lập trình hướng đối tượng với C# Trang 101 thừa gây ra.
Nếu khai báo của lớp Hinh trong ví dụ 3.2 được thay đổi từ khóa abstract bằng từ
khóa sealed (cũng có thể loại bỏ từ khóa trong các khai báo của phương thức ChuVi(),
DienTich(),…). Chương trình sẽ bị lỗi khi biên dịch. Nếu chúng ta cố thử biên dịch
chương trình thì sẽ nhận được lỗi từ trình biên dịch:
‘HinhTron’ cannot inherit from sealed class ‘Hinh’
Đây chỉ là một lỗi trong số những lỗi như ta không thể tạo một phương thức thành viên
protected trong một lớp khai báo là sealed. 7.3. Lớp Object
Tất cả các lớp của ngôn ngữ C# của bất cứ kiểu dữ liệu nào thì cũng được dẫn xuất từ
lớp System.Object. Thú vị là bao gồm cả các kiểu dữ liệu giá trị.
Một lớp cơ sở là cha trực tiếp của một lớp dẫn xuất. Lớp dẫn xuất này cũng có thể làm
cơ sở cho các lớp dẫn xuất xa hơn nữa, việc dẫn xuất này sẽ tạo ra một cây thừa kế
hay một kiến trúc phân cấp. Lớp gốc là lớp nằm ở trên cùng cây phân cấp thừa kế, còn
các lớp dẫn xuất thì nằm bên dưới. Trong ngôn ngữ C#, lớp gốc là lớp Object, lớp
này nằm trên cùng trong cây phân cấp các lớp.
Lớp Object cung cấp một số các phương thức dùng cho các lớp dẫn xuất có thể thực
hiện việc phủ quyết. Những phương thức này bao gồm Equals() kiểm tra xem hai đối
tượng có giống nhau hay không. Phương thức GetType() trả về kiểu của đối tượng. Và
phương thức ToString() trả về một chuỗi thể hiện lớp hiện hành. Sau đây là bảng tóm
tắt các phương thức của lớp Object. Phương thức Chức năng Equal( )
So sánh bằng nhau giữa hai đối tượng GetHashCode( )
Cho phép những đối tượng cung cấp riêng
những hàm băm cho sử dụng tập hợp. GetType( )
Cung cấp kiểu của đối tượng ToString( )
Cung cấp chuỗi thể hiện của đối tượng
Đề cương Lập trình hướng đối tượng với C# Trang 102 Finalize( ) Dọn dẹp các tài nguyên MemberwiseClone( )
Tạo một bản sao từ đối tượng.
Bảng 3.1: Tóm tắt các phương thức của lớp Object.
Ví dụ: Sau minh họa việc sử dụng phương thức ToString( ) thừa kế từ lớp Object. class HinhChuNhat { protected double a, b;
public HinhChuNhat(double a, double b) {
if ((a <= 0) | (b <= 0)) {
throw new ArithmeticException("Canh cua hinh chu nhat phai lon hon 0"); } else { this.a = a; this.b = b; } }
public override string ToString() { return "Hinh chu nhat"; } }
Trong tài liệu của lớp Object phương thức ToString() được khai báo như sau:
public virtual string ToString();
Đây là phương thức ảo public, phương thức này trả về một chuỗi và không nhận tham
số. Tất cả kiểu dữ liệu được xây dựng sẵn, như kiểu int, dẫn xuất từ lớp Object nên
nó cũng có thể thực thi các phương thức của lớp Object.
Lớp H inh Chu N hat trong ví dụ trên thực hiện việc phủ quyết phương thức
Đề cương Lập trình hướng đối tượng với C# Trang 103
ToString(), do đó phương thức này sẽ trả về giá trị có nghĩa. Nếu chúng ta không
phủ quyết phương thức ToString() trong lớp HinhChuNhat, phương thức của lớp cơ sở sẽ được thực thi
Như chúng ta thấy, hành vi mặc định đã trả về một chuỗi chính là tên của lớp đang thể
hiện. Các lớp không cần phải khai báo tường minh việc dẫn xuất từ lớp Object,
việc kế thừa sẽ được đưa vào một cách ngầm định. Như lớp HinhChuNhat trên ta
không khai báo bất cứ dẫn xuất của lớp nào nhưng C# sẽ tự động đưa lớp Object
thành lớp dẫn xuất. Do đó ta mới có thể phủ quyết phương thức ToString() của lớp Object.
Đề cương Lập trình hướng đối tượng với C# Trang 104 Bài 8: Giao diện 8.1. Giao diện
8.1.1. Xây dựng giao diện
Giao diện là ràng buộc, giao ước đảm bảo cho các lớp hay các cấu trúc sẽ
thực hiện một điều gì đó. Khi một lớp thực thi một giao diện, thì lớp này báo cho các
thành phần client biết rằng lớp này có hỗ trợ các phương thức, thuộc tính, sự kiện và
các chỉ mục khai báo trong giao diện.
Một giao diện đưa ra một sự thay thế cho các lớp trừu tượng để tạo ra các sự
ràng buộc giữa những lớp và các thành phần client của nó. Những ràng buộc này
được khai báo bằng cách sử dụng từ khóa interface, từ khóa này khai báo một kiểu
dữ liệu tham chiếu để đóng gói các ràng buộc. Một giao diện:
■ Gần giống như lớp trừu tượng, chỉ có phần khai báo và không có phần triển khai
■ Chỉ chứa phương thức, thuộc tính, chỉ mục và sự kiện
■ Thành phần của Interface mặc định là public abstract (virtual)
■ Không chứa thành phần tĩnh
■ Có thể triển khai từ một giao diện khác
■ Một lớp có thể triển khai từ nhiều giao diện
Một lớp trừu tượng được dùng làm lớp cơ sở cho một họ các lớp dẫn xuất từ nó.
Trong khi giao diện là sự trộn lẫn với các cây kế thừa khác.
Khi một lớp thực thi một giao diện, lớp này phải thực thi tất cả các phương
thức của giao diện. Đây là một bắt buộc mà các lớp phải thực hiện.
Cú pháp để định nghĩa một giao diện như sau:
[thuộc tính] [Bổ sung truy cập] interface [: danh sách cơ sở] {
Đề cương Lập trình hướng đối tượng với C# Trang 105 }
Phần thuộc tính chúng ta sẽ đề cập sau. Thành phần bổ sung truy cập bao
gồm: public, private, protected, internal, và protected internal, ý nghĩa tương tự
như các bổ sung truy cập của lớp.
Theo sau từ khóa interface là tên của giao diện. Thông thường tên của giao
diện được bắt đầu với từ I hoa (điều này không bắt buộc nhưng việc đặt tên như vậy
rất rõ ràng và dễ hiểu, tránh nhầm lẫn với các thành phần khác).
Danh sách cơ sở là danh sách các giao diện mà giao diện này mở rộng, Phần
thân của giao diện chính là phần thực thi giao diện sẽ được trình bày bên dưới.
Giả sử chúng ta muốn tạo một giao diện nhằm mô tả những phương thức và
thuộc tính của một lớp cần thiết để lưu trữ và truy cập từ một cơ sở dữ liệu hay các
thành phần lưu trữ dữ liệu khác như là một tập tin. Chúng ta quyết định gọi giao diện này là IStorage.
Trong giao diện này chúng ta xác nhận hai phương thức: Read() và Write(),
khai báo này sẽ được xuất hiện trong phần thân của giao diện như sau: interface IHinh { Double ChuVi(); Double DienTich(); void InThongTin(); }
Mục đích của một giao diện là để định nghĩa những khả năng mà chúng ta muốn có trong một lớp.
8.1.2. Khai báo thuộc tính của giao diện
Thuộc tính được khai báo trong giao diện không có phần thực thi cho get() và
set() mà chỉ đơn giản là khai báo có hành vi là get() và set(): Kieudulieu TenThuocTinh
Đề cương Lập trình hướng đối tượng với C# Trang 106 { [ get;] [ set;] }
Ví dụ: Trong giao diện Ihinh ta khai báo thuộc tính TenHinh chỉ trả về giá trị như sau: string TenHinh { get; }
8.1.3. Khai báo phương thức của giao diện
Khi định nghĩa các phương thức của giao diện không có phần bổ sung truy cập (ví dụ
như: public, protected, internal, private). Việc cung cấp các bổ sung truy cập sẽ tạo ra
một lỗi. Những phương thức của giao diện được ngầm định là public vì giao diện là
những ràng buộc được sử dụng bởi những lớp khác. Chúng ta không thể tạo một thể
hiện của giao diện, thay vào đó chúng ta sẽ tạo thể hiện của lớp có thực thi giao diện.
Khai báo phương thức theo cú pháp sau:
kieuDuLieu[void] TenPhuongThuc(khai báo các tham số);
Ví dụ 4.1: Xây dựng giao diện Ihinh interface IHinh {
// Khai báo thuộc tính của giao diện string TenHinh { get; }
// Khai báo các phương thức của giao diện
Đề cương Lập trình hướng đối tượng với C# Trang 107 double ChuVi(); double DienTich(); void InThongTin(); }
8.2. Thực thi giao diện
8.2.1. Thực thi giao diện
Khi xây dựng một lớp ta có thể kế thừa từ một lớp cơ sở và thực thi một hoặc
nhiều giao diện. Để thực thi giao diện ta đặt giao diện sau dấu : sau phần khai báo tên
lớp, nếu lớp kế thừa một lớp cơ sở và thực thi nhiều giao diện thì lớp cơ sở dứng trước
các giao diện, lớp cơ sở và giao diện ngăn cách nhau bởi dấu phẩy (,). Khi một lớp thực
thi một giao diện thì lớp đó phải thực thi tất cả các thuộc tính và phương thức khai báo
trong giao diện. Nếu không thực thi hết thì chương trình dịch sẽ báo lỗi
Ví dụ 4.1 (tiếp) thực thi giao diện using System; namespace LTHDTC { class Diem { protected int x,y; public Diem (int x, int y) { this.x=x; this.y=y; } public int X { get { return x; } set
Đề cương Lập trình hướng đối tượng với C# Trang 108 { x = value; } } public int Y { get { return y; } set { y = value; } } } interface IHinh { string TenHinh { get; } double ChuVi(); double DienTich(); void InThongTin(); } class HinhTron:IHinh { Diem tam; double bankinh;
public HinhTron(int x, int y, double bankinh) { tam = new Diem(x, y);
Đề cương Lập trình hướng đối tượng với C# Trang 109 this.bankinh = bankinh; } public string TenHinh { get { return "Hinh Tron"; } } public double ChuVi() { return Math.PI * bankinh * 2; } public double DienTich() {
return Math.PI * bankinh * bankinh; } public void InThongTin() {
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh, DienTich(), ChuVi()); } } }
Truy cập phương thức giao diện
Chúng ta có thể truy cập những thành viên của giao diện IHinh như thể là các thành viên của lớp HinhTron
HinhTron h = new HinhTron(4, 7, 4); h. InThongTin();
hay là ta có thể tạo thể hiện của giao diện bằng cách gán đối tượng HinhTron cho một
kiểu dữ liệu giao diện, và sau đó sử dụng giao diện này để truy cập các phương thức:
HinhTron h = new HinhTron(4, 7, 4); IHinh hinh = (IHinh)h; Hinh. InThongTin();
Ghi chú: Chúng ta không thể tạo thể hiện của giao diện một cách trực tiếp.Do đó
Đề cương Lập trình hướng đối tượng với C# Trang 110
chúng ta không thể thực hiện như sau: Ihinh hinh=new IHinh();
Tuy nhiên chúng ta có thể tạo thể hiện của lớp thực thi giao diện như sau:
HinhTron h = new HinhTron(4, 7, 4);
Sau đó chúng ta có thể tạo thể hiện của giao diện bằng cách gán đối tượng thực thi
đến kiểu dữ liệu giao diện, trong trường hợp này là IHinh IHinh hinh= (IHinh) h;
Chúng ta có thể kết hợp những bước trên như sau:
IHinh hinh= (IHinh) new HinhTron(4, 7, 4);
Nói chung, cách thiết kế tốt nhất là quyết định truy cập những phương thức của
giao diện thông qua tham chiếu của giao diện. Do vậy cách tốt nhất là sử dụng
hinh.InThongTin() hơn là sử dụng h.InThongTin() trong ví dụ trước. Truy cập thông
qua giao diện cho phép chúng ta đối xử giao diện một cách đa hình. Nói cách khác,
chúng ta tạo hai hay nhiều hơn những lớp thực thi giao diện, và sau đó bằng cách truy
cập lớp này chỉ thông qua giao diện.
Gán đối tượng cho một giao diện
Trong nhiều trường hợp, chúng ta không biết trước một đối tượng có hỗ trợ
một giao diện đưa ra. Giả sử ta viết thêm một giao diện Ithu nào đó nhưng lớp
HinhTron của chúng ta không triển khai giao diện này thì phéo gán sau sẽ không hợp lệ Ithu t=(IThu)h;
Khi biên dịch chương trình thì dòng lệnh trên không hề bị báo lỗi vì Ithu là một giao
diện hoàn toàn hợp lệ, Tuy nhiên khi chạy chương trình sẽ tạo ra một ngoại lệ
(exception). Và chương trình dừng lại.
Giao diện đối lập với lớp trừu tượng
Giao diện rất giống như các lớp trừu tượng. Thật vậy, chúng ta có thể thay thế
khai báo của IHinh trở thành một lớp trừu tượng: abstract class IHinh {
Đề cương Lập trình hướng đối tượng với C# Trang 111 abstract public void ChuVi();
abstract public void DienTich(); }
Bây giờ lớp HinhChuNhat có thể thừa kế từ lớp trừu tượng IHinht, và cũng không có
gì khác nhiều so với việc sử dụng giao diện.
Tuy nhiên, giả sử chúng ta mua một lớp ClsDaGiac từ một hãng thứ ba và chúng ta
muốn kết hợp với lớp có sẵn như IHinh. Tuy nhiên trong ngôn ngữ C# chúng ta
không cho phép thực hiện đa kế thừa.
Tuy nhiên, ngôn ngữ C# cho phép chúng ta thực thi bất cứ những giao diện nào và
dẫn xuất từ một lớp cơ sở. Do đó, bằng cách làm cho IHinh là một giao diện, chúng
ta có thể kế thừa từ lớp ClsDaGiac và cũng từ giao diện IHinh. Ta có thể tạo lớp HinhChuNhat như sau:
public class HinhChuNhat: ClsDaGiac, IHinh { // Code của lớp }
8.2.2. Toán tử is
Chúng ta muốn kiểm tra một đối tượng xem nó có hỗ trợ giao diện, để sau đó thực
hiện các phương thức tương ứng. Trong ngôn ngữ C# có hai cách để thực hiện điều
này. Phương pháp đầu tiên là sử dụng toán tử is.
Cú pháp của toán tử is là: is
Toán tử is trả về giá trị true nếu biểu thức thường là kiểu tham chiếu có thể được gán
an toàn đến kiểu dữ liệu cần kiểm tra mà không phát sinh ra bất cứ ngoại lệ nào.
Ví dụ 4.2 minh họa việc sử dụng toán tử is để kiểm tra HinhTron có thực thi giao diện Ihinh hay không class Chay
Đề cương Lập trình hướng đối tượng với C# Trang 112 { static void Main() {
HinhTron h = new HinhTron(4, 7, 4);
// Phép gán này không an toàn có thể gây lỗi cho chương trình nếu lớp HinhTron //không thực thi giao diện IHinh IHinh hinh1 = (IHinh)h; if (h is IHinh) { IHinh hinh = (IHinh)h; // Phép gán này an toàn hinh.InThongTin();
Console.WriteLine("Hinh thuc thi IHinh"); } else h.InThongTin(); Console.Read(); } }
Trong ví dụ 4.2, hàm Main() lúc này sẽ thực hiện việc gán với interface khi được
kiểm tra hợp lệ. Việc kiểm tra này được thực hiện bởi câu lệnh if: if ( doc is IStorable )
Biểu thức điều kiện sẽ trả về giá trị true và phép gán sẽ được thực hiện khi đối tượng
có thực thi giao diện bên phải của toán tử is.
Tuy nhiên, việc sử dụng toán tử is đưa ra một việc không có hiệu quả. Để hiểu được
điều này, chúng ta xem đoạn chương trình được biên dịch ra mã IL. Ở đây sẽ có một
ngoại lệ nhỏ, các dòng bên dưới là sử dụng hệ thập lục phân: IL_0023: isinst IHinh IL_0028: brfalse.s IL_0039 IL_002a: ldloc.0
Đề cương Lập trình hướng đối tượng với C# Trang 113 IL_002b: castclass IHinh IL_0030: stloc.2 IL_0031: ldloc.2 IL_0032: callvirt
instance void IHinh::InThongTin() IL_0037: br.s IL_0043 IL_0039: ldstr
“Compressible not supported”
Điều quan trọng xảy ra là khi phép kiểm tra IHinh ở dòng ” if (h is IHinh)
”. Từ khóa isinst là mã MSIL tương ứng với toán tử is. Nếu việc kiểm tra đối tượng
(h) đúng kiểu của kiểu bên phải. Thì chương trình sẽ chuyển đến dòng lệnh tiếp theo
để thực hiện tiếp và castclass được gọi. Điều không may là castcall cũng kiểm tra kiểu
của đối tượng. Do đó việc kiểm tra sẽ được thực hiện hai lần. Giải pháp hiệu quả hơn là
việc sử dụng toán tử as.
8.2.3. Toán tử as
Toán tử as kết hợp toán tử is và phép gán bằng cách đầu tiên kiểm tra hợp lệ
phép gán (kiểm tra toán tử is trả về true) rồi sau đó phép gán được thực hiện. Nếu phép
gán không hợp lệ (khi phép gán trả về giá trị false), thì toán tử as trả về giá trị null.
Ghi chú: Từ khóa null thể hiện một tham chiếu không tham chiếu đến đâu cả
(null reference). Đối tượng có giá trị null tức là không tham chiếu đến bất kỳ đối tượng nào.
Sử dụng toán tử as để loại bỏ việc thực hiện các xử lý ngoại lệ. Đồng thời cũng né
tránh việc thực hiện kiểm tra dư thừa hai lần. Do vậy, việc sử dụng tối ưu của phép gán
cho giao diện là sử dụng as.
Cú pháp sử dụng toán tử as như sau: as
Đoạn chương trình sau thay thế việc sử dụng toán tử is bằng toán tử as và sau đó
thực hiện việc kiểm tra xem giao diện được gán có null hay không: static void Main() {
Đề cương Lập trình hướng đối tượng với C# Trang 114
HinhTron h = new HinhTron(4, 7, 4); IHinh hinh = h as IHinh; if (h != null) { hinh.InThongTin();
Console.WriteLine("Hinh thuc thi IHinh"); } else h.InThongTin(); Console.Read(); }
Ta có thể so sánh đoạn mã IL sau với đoạn mã IL sử dụng toán tử is trước sẽ thấy
đoạn mã sau có nhiều hiệu quả hơn: IL_0023: isinst IHinh IL_0028: stloc.2 IL_0029: ldloc.2 IL_002a: brfalse.s IL_0034 IL_002c: ldloc.2 IL_002d: callvirt
instance void IHinh::InThongTin()
Ghi chú: Nếu mục đích của chúng ta là kiểm tra một đối tượng có hỗ trợ một giao
diện và sau đó là thực hiện việc gán cho một giao diện, thì cách tốt nhất là sử dụng
toán tử as là hiệu quả nhất. Tuy nhiên, nếu chúng ta chỉ muốn kiểm tra kiểu dữ liệu
và không thực hiện phép gán ngay lúc đó. Có lẽ chúng ta chỉ muốn thực hiện việc
kiểm tra nhưng không thực hiện việc gán, đơn giản là chúng ta muốn thêm nó vào
danh sách nếu chúng thực sự là một giao diện. Trong trường hợp này, sử dụng toán tử
is là cách lựa chọn tốt nhất.
8.3. Thực thi giao diện
8.3.1. Thực thi giao diện
Khi thực thi một lớp chúng ta có thể tự do đánh dấu bất kỳ hay tất cả các
phương thức thực thi giao diện như là một phương thức ảo. Ví dụ, lớp HinhChuNhat
Đề cương Lập trình hướng đối tượng với C# Trang 115
thực thi giao diện IHinh và có thể đánh dấu InThongTin() như là phương thức ảo.
Những người phát triển sau có thể dẫn xuất một kiểu dữ liệu mới từ lớp HinhTron, có
thể là lớp HinhVuong, và những người này mong muốn lớp HinhVuong thực thi
phương thức InThongTin() riêng của lớp hình vuông. Ví dụ 4.3 using System; namespace LTHDTC {
//Xây dựng giao diện IHinh interface IHinh { string TenHinh { get; } double ChuVi(); double DienTich(); void InThongTin(); }
//Lớp HinhChuNhat triển khai giao diện IHinh class HinhChuNhat : IHinh {
protected double chieuDai, chieuRong;
// Thực thi phương thức của giao diện
public HinhChuNhat(double chieuDai, double chieuRong) { this.chieuDai = chieuDai; this.chieuRong = chieuRong; }
// Thực thi phương thức của giao diện và khai báo phương thức là phương //thức ảo,
để các lớp kế thừa lớp có thể tự do thực thi phương thức của riêng mình/
public virtual void InThongTin() {
Đề cương Lập trình hướng đối tượng với C# Trang 116
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh, DienTich(), ChuVi()); } public double ChuVi() {
return 2 * (chieuRong + chieuDai); } public double DienTich() { return chieuDai * chieuRong; } public virtual string TenHinh {
get { return "Hinh chu nhat"; } } }
// Lớp HinhVuong kế thừa từ HinhChuNhat có thể ghi đè các phương thức ảo của //lớp cơ sở class HinhVuong : HinhChuNhat { public HinhVuong(double canh) : base(canh, canh) { }
public override string TenHinh { get { return "Hinh Vuong"; } }
// Ghi đè phương thức ảo của lớp cơ sở
public override void InThongTin() { base.InThongTin();
Console.WriteLine("Day la lop hinh vuong da ghi de");
Đề cương Lập trình hướng đối tượng với C# Trang 117 } } class Chay { static void Main() {
IHinh hv = new HinhVuong(4) as IHinh; if (hv != null) { hv.InThongTin(); } Console.Read(); } } }
Do lớp HinhVuong kế thừa từ HinhChuNhat, mà HinhChuNhat lại triển khai giao diện
Ihinh, nên để thực thi phương thức InThongTin ta có 4 cách, đó là những cách nào?
+ Trực tiếp từ đối tượng HinhVuong:
HinhVuong hv = new HinhVuong(4); hv.InThongTin();
+ Thông qua đối tượng HinhChuNhat:
HinhChuNhat hv = (HinhChuNhat)new HinhVuong(4); hv.InThongTin();
+ Thông qua giao diện mà lớp cơ sở của nó triển khai
HinhVuong hv = new HinhVuong(4); IHinh ihv = hv as IHinh; ihv.InThongTin();
+ Thông qua giao diện mà lớp cơ sở của nó triển khai HinhChuNhat h;
HinhVuong hv = new HinhVuong(4); h=hv;
Đề cương Lập trình hướng đối tượng với C# Trang 118 IHinh ihv = h as IHinh; ihv.InThongTin();
Thực thi giao diện tường minh
Trong việc thực thi giao diện cho tới giờ, những lớp thực thi tạo ra các phương
thức thành viên cùng ký hiệu và kiểu trả về như là phương thức được mô tả trong giao
diện. Chúng ta không cần thiết khai báo tường minh rằng đây là một thực thi của một
giao diện, việc này được hiểu ngầm bởi trình biên dịch.
Tuy nhiên, có vấn đề xảy ra khi một lớp thực thi hai giao diện và cả hai giao diện này
có các phương thức cùng một ký hiệu. Ví dụ 4.4 tạo ra hai giao diện: IHinh và IDaGiac.
Sau đó thực thi phương thức InThongTin() trong giao diện IDiem phương thức
InThongTin() để in thông tin về đa giác. Không may là phương thức này sẽ tranh chấp
với phương thức InThongTin () của IHinh mà phải thực thi.
Bởi vì cả hai giao diện cùng có một phương thức InThongTin,việc thực thi lớp
HinhChuNhat phải sử dụng thực thi tường minh cho mỗi phương thức. Với việc thực
thi tường minh, lớp thực thi HinhChuNhat sẽ khai báo tường minh cho mỗi phương thức: void IHinh.InThongTin ();
Điều này sẽ giải quyết việc tranh chấp, nhưng nó sẽ tạo ra hàng loạt các hiệu ứng thú vị.
+ Với các phương thức khác thì không cần thiết phải sử dụng thực thi tường minh
vì không có sự tranh chấp cho nên ta khai báo như thông thường.
+ Phương thức thực thi tường minh không có bổ sung truy cập:
void IHinh.InThongTin (); phương thức này được hiểu ngầm định là public
Thật vậy, một phương thức được khai báo tường minh thì sẽ không được khai báo với
các từ khóa bổ sung truy cập: abstract, virtual, override, và new.
+ Chúng ta không thể truy cập phương thức thực thi tường minh thông qua chính đối tượng,
HinhChuNhat h=new HinhChuNhat(3,4);
Đề cương Lập trình hướng đối tượng với C# Trang 119 h.InThongTin();
Trình biên dịch sẽ báo lỗi Error 1
'LTHDTC.HinhChuNhat' does not contain a definition for 'InThongTin'
and no extension method 'InThongTin' accepting a first argument of type
'LTHDTC.HinhChuNhat' could be found (are you missing a using directive or an assembly reference?)
C:\Tai lieu giang day\Lap trinh huong doi tuong\Vi du\LTHDTC\LTHDTC\clsHinh.cs 171 15 LTHDTC
Khi đó muốn gọi phương thức InThongTin thì ta phải gán biến đối tượng HinhChuNhat
cho một đối tượng của giao static void Main() {
HinhChuNhat h = new HinhChuNhat(3, 4); h.InThongTin(); IDaGiac d = h as IDaGiac; if (d != null) { d.InThongTin(); } Console.Read(); }
Sử dụng thực thi tường minh được áp dụng trong ví dụ 4.4
Ví dụ 4.4: Thực thi tường minh. using System; namespace LTHDTC { interface IDaGiac { void InThongTin(); } interface IHinh
Đề cương Lập trình hướng đối tượng với C# Trang 120 { string TenHinh { get; } double ChuVi(); double DienTich(); void InThongTin(); } class HinhVuong : HinhChuNhat { public HinhVuong(double canh) : base(canh, canh) { }
public override string TenHinh { get { return "Hinh Vuong"; } } public void InThongTin() {
Console.WriteLine("Day la lop hinh vuong da ghi de"); } }
class HinhChuNhat : IHinh, IDaGiac {
protected double chieuDai, chieuRong;
public HinhChuNhat(double chieuDai, double chieuRong) {
Đề cương Lập trình hướng đối tượng với C# Trang 121 this.chieuDai = chieuDai; this.chieuRong = chieuRong; }
// Phương thức thực thi tường minh của giao diện IHinh void IHinh.InThongTin () {
Console.WriteLine("{0} co dien tich la: {1}; chu vi la: {2}", TenHinh, DienTich(), ChuVi()); }
// Phương thức thực thi tường minh của giao diện IDaGiac void IDaGiac.InThongTin() {
Console.WriteLine("{0} là tứ giác có các góc bằng 90 độ",TenHinh ); } public double ChuVi() {
return 2 * (chieuRong + chieuDai); } public double DienTich() { return chieuDai * chieuRong; } public virtual string TenHinh {
get { return "Hinh chu nhat"; } } } } class Chay { static void Main() {
Đề cương Lập trình hướng đối tượng với C# Trang 122
HinhChuNhat h = new HinhChuNhat(3, 4); h.InThongTin(); IDaGiac d = h as IDaGiac; if (d != null) { d.InThongTin(); } Console.Read(); } } }
Lựa chọn việc thể hiện phương thức giao diện
Những người thiết kế lớp có thể thu được lợi khi một giao diện được thực thi thông
qua thực thi tường minh và không cho phép các thành phần client của lớp truy cập
trừ phi sử dụng thông qua việc gán cho giao diện.
Giả sử nghĩa của đối tượng HinhChuNhat chỉ ra rằng nó thực thi giao diện
IHinh, nhưng không muốn phương thức InThongTin() là phần giao diện public
của lớp HinhChuNhat. Chúng ta có thể sử dụng thực thi tường minh để chắc chắn
chỉ có thể truy cập thông qua việc gán cho giao diện. Điều này cho phép chúng ta
lưu trữ ngữ nghĩa của lớp HinhChuNhat trong khi vẫn có thể thực thi được giao
diện IHinh. Nếu thành phần client muốn đối tượng thực thi giao diện IHinh, nó có
thể thực hiện gán tường minh cho giao diện để gọi các phương thức thực thi giao
diện. Nhưng khi sử dụng đối tượng HinhChuNhat thì nghĩa là không có phương thức InThongTin().
Thật vậy, chúng ta có thể lựa chọn thể hiện những phương thức thông qua
thực thi tường minh, do đó chúng ta có thể trưng bày một vài phương thức thực thi
như là một phần của lớp HinhChuNhat và một số phương thức khác thì không.
Trong ví dụ 4.4, đối tượng HinhChuNhat trưng bày phương thức ChuVi(),
DienTich() như là phương thức của lớp HinhChuNhat, nhưng phương thức
InThongTin() chỉ được thể hiện thông qua gán cho giao diện. Thậm chí nếu
IHinh không có phương thức InThongTin(), chúng ta cũng có thể chọn thực thi
tường minh phương thức InThongTin() để phương thức không được thể hiện ra bên
Đề cương Lập trình hướng đối tượng với C# Trang 123
ngoài như các phương thức của HinhChuNhat.
Chúng ta lưu ý rằng vì thực thi giao diện tường minh ngăn ngừa việc sử dụng từ
khóa virtual, một lớp dẫn xuất có thể được hỗ trợ để thực thi lại phương thức. Do
đó, nếu HinhVuong dẫn xuất từ HinhChuNhat, nó có thể được thực thi lại phương
thức HinhChuNhat.InThongTin () bởi vì lớp HinhChuNhat thực thi phương thức
InThongTin() không phải ảo.
Ẩ n thành viên
Ngôn ngữ C# cho phép ẩn các thành viên của giao diện. Ví dụ, chúng ta có một giao diện
IBase với một thuộc tính P: interface IBase { int P { get; set;} }
và sau đó chúng ta dẫn xuất từ giao diện này ra một giao diện khác, IDerived, giao
diện mới này làm ẩn thuộc tính P với một phương thức mới P(): interface IDerived : IBase { new int P(); }
Việc cài đặt này là một ý tưởng tốt, bây giờ chúng ta có thể ẩn thuộc tính P trong lớp
cơ sở. Một thực thi của giao diện dẫn xuất này đòi hỏi tối thiểu một thành viên
giao diện tường minh. Chúng ta có thể sử dụng thực thi tường minh cho thuộc tính
của lớp cơ sở hoặc của phương thức dẫn xuất, hoặc chúng ta có thể sử dụng thực thi
tường minh cho cả hai. Do đó, ba phiên bản được viết sau đều hợp lệ: class myClass : IDerived {
Đề cương Lập trình hướng đối tượng với C# Trang 124
// thực thi tường minh cho thuộc tính cơ sở int IBase.p { get{...}}
// thực thi ngầm định phương thức dẫn xuất public int P() {...} } class myClass : IDerived {
// thực thi ngầm định cho thuộc tính cơ sở public int P { get{...}}
// thực thi tường minh phương thức dẫn xuất int IDerived.P() {...} } class myClass : IDerived {
// thực thi tường minh cho thuộc tính cơ sở int IBase.P { get{...}}
// thực thi tường minh phương thức dẫn xuất int IDerived.P(){...} }
8.3.2. Mở rộng giao diện
C# cung cấp chức năng cho chúng ta mở rộng một giao diện đã có bằng cách
thêm các phương thức và các thành viên hay bổ sung cách làm việc cho các thành viên.
Ví dụ, chúng ta có thể mở rộng giao diện IHinh với một giao diện mới là IHinhs.
Giao diện mới này mở rộng giao diện cũ bằng cách thêm phương thức Tinhchat
Đề cương Lập trình hướng đối tượng với C# Trang 125 Interface Ihinhs:IHinh { Void TinhChat(); }
Các lớp khác có thể thực thi tự do giao diện IHinh hay Ihinhs tùy thuộc vào mục
đích có cần thêm chức năng hay không. Nếu một lớp thực thi giao diện IHinhs,
thì lớp này phải thực thi tất cả các phương thức của cả hai giao diện IHinh và giao
diện IHinhs. Những đối tượng của lớp thực thi giao diện IHinhs có thể được gán
cho cả hai giao diện IHinhs và IHinh.
Kết hợp các giao diện
Một cách tương tự, chúng ta có thể tạo giao diện mới bằng cách kết hợp các
giao diện cũ và ta có thể thêm các phương thức hay các thuộc tính cho giao diện mới.
Ví dụ, chúng ta quyết định tạo một giao diện IhinhDaGiac Giao diện mới này sẽ kết
hợp những phương thức của cả hai giao diện IHinh và IDa Giac và cũng thêm
vào một phương thức mới So Canh().
int er face Ihinh Da G iac: I H in h, IDa Giac { int So Canh(); }
Khi đó nếu lớp nào lớp nào triển khai giao diện IhinhDaGiac thì sẽ phải triển khai tất cả
các phương thức, thuộc tính của cả hai giao diện IHinh và IdaGiac và phương thức bổ sung SoCanh.
8.3.3. Tham chiếu đối tượng của lớp con qua giao diện
Như đã trình bày trong các mục trước, chúng ta thường sử dụng giao diện để thực thi
các phương thức của đối tượng lớp triển khai từ giao diện, điều đó giúp chúng ta đa hình. Class chay
Đề cương Lập trình hướng đối tượng với C# Trang 126 {
static void ThaoTacHinh(IHinh i) { i.InThongTin(); } static void Main() {
ThaoTacHinh(new HinhChuNhat(3,4));
ThaoTacHinh(new HinhTron(3,6,8));
ThaoTacHinh(new HinhVuong(7)); } }
8.3.4. Giao diện đối lập với lớp trừu tượng
Giao diện rất giống như các lớp trừu tượng. Thật vậy, chúng ta có thể thay thế
khai báo của IStorable trở thành một lớp trừu tượng: abstract class Storable { abstract public void Read(); abstract public void Write(); }
Bây giờ lớp Document có thể thừa kế từ lớp trừu tượng IStorable, và cũng không có gì
khác nhiều so với việc sử dụng giao diện.
Tuy nhiên, giả sử chúng ta mua một lớp List từ một hãng thứ ba và chúng ta muốn
kết hợp với lớp có sẵn như Storable. Trong ngôn ngữ C++ chúng ta có thể tạo ra một
lớp StorableList kế thừa từ List và cả Storable. Nhưng trong ngôn ngữ C# chúng
ta không thể làm được, chúng ta không thể kế thừa từ lớp trừu tượng Storable và từ
lớp List bởi vì trong C# không cho phép thực hiện đa kế thừa từ những lớp.
Tuy nhiên, ngôn ngữ C# cho phép chúng ta thực thi bất cứ những giao diện nào và
dẫn xuất từ một lớp cơ sở. Do đó, bằng cách làm cho Storable là một giao diện,
chúng ta có thể kế thừa từ lớp List và cũng từ IStorable. Ta có thể tạo lớp StorableList
Đề cương Lập trình hướng đối tượng với C# Trang 127 như sau:
public class StorableList : List, IStorable {
// phương thức List........ public void Read() {...} public void Write( object o) {...} //.... }
8.4 Kỹ thuật xử lý ngoại lệ
8.4.1 Khái niệm xử lý ngoại lệ
a. Đón bắt lỗi với try và catch
Xét đoạn chương trình sau: static void Main() { int i;
Console.Write("Nhap gia tri cho i: ");
i = int.Parse(Console.ReadLine());
Console.WriteLine("Giá tri i vừa nhập " + i); }
Chương trình trên khi dịch và chạy chương trình sẽ chẳng có vấn đề gì xảy ra, nếu
người sử dụng nhập đúng giá trị cho biến i. Nhưng đoạn chương trình trên sẽ bị tạm
ngừng không chạy được nếu người sử dụng không nhập vào một giá trị số mà nhập vào
một chữ cái ‘a’ chẳng hạn. Khi đó lỗi phát sinh của chương trình là lỗi có nguyên nhân
do phía hình động của người sử dụng làm phát sinh một ngoại lệ chứ không phải là lỗi lập trình.
Ngôn ngữ C# cũng giống như bất cứ ngôn ngữ hướng đối tượng khác, cho phép
Đề cương Lập trình hướng đối tượng với C# Trang 128
xử lý những lỗi và các điều kiện không bình thường với những ngoại lệ. Ngoại lệ là
một đối tượng đóng gói những thông tin về sự cố của một chương trình không bình thường.
Một điều quan trọng để phân chia giữa bug, lỗi, và ngoại lệ. Một bug là một lỗi
lập trình có thể được sửa chữa trước khi mã nguồn được chuyển giao. Những ngoại lệ
thì không được bảo vệ và tương phản với những bug. Mặc dù một bug có thể là nguyên
nhân sinh ra ngoại lệ, chúng ta cũng không dựa vào những ngoại lệ để xử lý những
bug trong chương trình, tốt hơn là chúng ta nên sửa chữa những bug này.
Khi chúng ta thực hiện chương trình thì có thể có những ngoại lệ phát sinh trong
quá trình chạy do một tác động nào đó mà nguyên nhân không phải lỗi lập trình, những
lỗi này với người lập trình có kinh nghiệm họ có thể dự đoán trước được những lỗi phát
sinh khi chạy chương trình. Khi một ngoại lệ được tạo ra, việc thực thi của các chức
năng hiện hành sẽ bị treo cho đến khi nào việc xử lý ngoại lệ tương ứng được tìm thấy.
Điều này có nghĩa rằng nếu chức năng hoạt động hiện hành không thực hiện việc
xử lý ngoại lệ, thì chức năng này sẽ bị chấm dứt và hàm gọi sẽ nhận sự thay đổi
đến việc xử lý ngoại lệ. Nếu hàm gọi này không thực hiện việc xử lý ngoại lệ, ngoại
lệ sẽ được xử lý sớm bởi CLR, điều này dẫn đến chương trình của chúng ta sẽ kết thúc.
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.
Một cách lý tưởng thì 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 đề 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ó 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.
Cấu trúc xử dụng khối try …catch để xử lý ngoại lệ như sau: try {
// Đoạn chương trình có khả năng phát sinh ngoại lệ trong quá trình chạy } Catch (KieuNgoaiLe bien) {
// Lệnh xử lý khi đoạn lệnh bị phát sinh lỗi chương trình sẽ chuyển sang //thực
Đề cương Lập trình hướng đối tượng với C# Trang 129
hiện các câu lệnh ở đây/ } Catch (KieuNgoaiLe bien) {
// Lệnh xử lý khi đoạn lệnh bị phát sinh lỗi chương trình sẽ chuyển sang //thực
hiện các câu lệnh ở đây/ } … Finally {
// Lệnh xử lý cho dù có ngoại lệ hay không có ngoại lệ phát sinh. }
Ví dụ 25.1 xử lý bắt ngoại lệ bằng try…catch static void Main() { try { int i;
Console.Write("Nhap gia tri cho i: ");
i = int.Parse(Console.ReadLine());
Console.WriteLine("Giá tri i vừa nhập " + i); } catch {
Console.WriteLine("Co loi phat sinh"); } Console.ReadLine(); }
b. Dọn dẹp ngoài lệ với try và finally
Trong một số tình huống, việc phát sinh ngoại lệ và unwind stack có thể tạo ra một
số vấn đề. Ví dụ như nếu chúng ta mở một tập tin hay trường hợp khác là xác nhận
Đề cương Lập trình hướng đối tượng với C# Trang 130
một tài nguyên, chúng ta có thể cần thiết một cơ hội để đóng một tập tin hay là giải
phóng bộ nhớ đệm mà chương trình đã chiếm giữ trước đó.
Ghi chú: Trong ngôn ngữ C#, vấn đề này ít xảy ra hơn do cơ chế thu dọn tự động của C#
ngăn ngừa những ngoại lệ phát sinh từ việc thiếu bộ nhớ.
Tuy nhiên, có một số hành động mà chúng ta cần phải quan tâm bất cứ khi nào một
ngoại lệ được phát sinh ra, như việc đóng một tập tin, chúng ta có hai chiến lược để
lựa chọn thực hiện. Một hướng tiếp cận là đưa hành động nguy hiểm vào trong khối try
và sau đó thực hiện việc đóng tập tin trong cả hai khối catch try. Tuy nhiên, điều
này gây ra đoạn chương trình không được đẹp do sử dụng trùng lặp lệnh. Ngôn ngữ
C# cung cấp một sự thay thế tốt hơn trong khối finally.
Đoạn chương trình bên trong khối catch được đảm bảo thực thi mà không quan
tâm đến việc khi nào thì một ngoại lệ được phát sinh. Phương thức TestFunc() trong ví
dụ 5.2 minh họa việc mở một tập tin như là hành động đầu tiên của nó, sau đó phương
thức thực hiện một vài các phép toán toán học, và sau đó là tập tin được đóng. Có thể
trong quá trình mở tập tin cho đến khi đóng tập tin chương trình phát sinh ra một
ngoại lệ. Nếu xuất hiện ngoại lệ, và khi đó tập tin vẫn còn mở. Người phát triển biết
rằng không có chuyện gì xảy ra, và cuối của phương thức này thì tập tin sẽ được
đóng. Do chức năng đóng tập tin được di chuyển vào trong khối finally, ở đây nó sẽ
được thực thi mà không cần quan tâm đến việc có phát sinh hay không một ngoại lệ trong chương trình.
Ví dụ 5.2: Sử dụng khối finally. class NgoaiLe { public void MoTep() {
StreamWriter f = new StreamWriter(new FileStream("ketqua.txt", FileMode.Append)); try { int x, y;
Đề cương Lập trình hướng đối tượng với C# Trang 131
Console.Write("Gia tri x: "); x = int.Parse(Console.ReadLine());
Console.Write("Gia tri y: "); y = int.Parse(Console.ReadLine()); double z = (double)x / y;
Console.Write("Nhap vao mot so: ");
int so = int.Parse(Console.ReadLine());
f.WriteLine("{0}/{1}={2}", x, y, z); //f.Close(); }
// Các câu lệnh đặt trong khối catch được xử lý khi có ngoại lệ xảy ra. catch(Exception ex) {
Console.WriteLine(ex.Message); //f.Close(); }
// Các câu lệnh đặt trong khối finally sẽ được xử lý dù có hay không có ngoại lệ phát sinh. Finally { f.Close(); }
StreamReader fd = new StreamReader(new FileStream("ketqua.txt", FileMode.Open));
Console.WriteLine(fd.ReadToEnd()); fd.Close(); Console.ReadLine(); } } class chay { static void Main() { NgoaiLe f = new NgoaiLe(); f.MoTep(); }
Đề cương Lập trình hướng đối tượng với C# Trang 132 }
c. Kiểm soát tất cả ngoại lệ với try-catch-finally
Với một đoạn chương trình có thể xảy ra nhiều ngoại lệ khác nhau tùy vào từng trường
hợp, chúng ta muốn ứng xử khác nhau tùy vào từng trường hợp khác nhau khi đó chúng
ta sử dụng nhiều khối catch. Mỗi một khối catch bắt một kiểu ngoại lệ khác nhau, và lưu
ý rằng kiểu ngoại lệ cha phải đặt sau ngoại lệ con. Các câu lệnh được xử lý dù có hay
không có ngoại lệ xảy ra chúng ta đặt trong khối finally.
Ví dụ 5.3: public void NhapSo() { int x, y,z=0; try {
Console.Write("Nhap gia tri x: ");
x = int.Parse(Console.ReadLine());
Console.Write("Nhap gia tri y: ");
y = int.Parse(Console.ReadLine()); z = x / y; }
catch (DivideByZeroException ex) {
Console.WriteLine(ex.Message );
Console.WriteLine("gia tri mac dinh z =0"); z = 0; } catch (FormatException ex) {
Console.WriteLine(ex.Message );
Console.WriteLine("Gia tri mac dinh cua z=1"); z = 1; } finally { Console.WriteLine("z= " +z); } Console.ReadLine(); }
8.4.2. Các lớp ngoại lệ của .Net
Đối tượng System.Exception cung cấp một số các phương thức và thuộc tính hữu
dụng. Thuộc tính Message cung cấp thông tin về ngoại lệ, như là lý do tại sao ngoại lệ
được phát sinh. Thuộc tính Message là thuộc tính chỉ đọc, đoạn chương trình phát
sinh ngoại lệ có thể thiết lập thuộc tính Message như là một đối mục cho bộ khởi
dựng của ngoại lệ. Thuộc tính HelpLink cung cấp một liên kết để trợ giúp cho các tập
tin liên quan đến các ngoại lệ. Đây là thuộc tính chỉ đọc. Thuộc tính StackTrace cũng
là thuộc tính chỉ đọc và được thiết lập bởi CLR. Trong ví dụ 5.4 thuộc tính
Exception.HelpLink được thiết lập và truy cập để cung cấp thông tin cho người sử
dụng về ngoại lệ DivideBy-ZeroException. Thuộc tính StackTrace của ngoại lệ được
sử dụng để cung cấp thông tin stack cho câu lệnh lỗi. Một thông tin stack cung cấp
hàng loạt các cuộc gọi stack của phương thức gọi mà dẫn đến những ngoại lệ được phát sinh.
Bảng sau mô tả một số các lớp ngoại lệ chung được khai báo bên trong namespace System.
CÁC LỚP NGOẠI LỆ Tên ngoại lệ Mô tả MethodAccessException
Lỗi truy cập, do truy cập đến thành viên
hay phương thức không được truy cập ArgumentException Lỗi tham số đối mục ArgumentNullException
Đối mục Null, phương thức được truyền đối
mục null không được chấp nhận. ArithmeticException
Lỗi liên quan đến các phép toán ArrayTypeMismatchException
Kiểu mảng không hợp, khi cố lưu trữ kiểu
không thích hợp vào mảng DivideByZeroException Lỗi chia zero FormatException
Định dạng không chính xác một đối mục nào đó IndexOutOfRangeException
Chỉ số truy cập mảng không hợp lệ, dùng nhỏ
hơn chỉ số nhỏ nhất hay lớn hơn chỉ số lớn nhất của mảng InvalidCastException Phép gán không hợp lệ
MulticastNotSupportedException
Multicast không được hỗ trợ, do việc kết hợp hai delegate không đúng NotFiniteNumberException
Không phải số hữu hạn, số không hợp lệ NotSupportedException
Phương thức không hỗ trợ, khi gọi một
phương thức không tồn tại bên trong lớp. NullReferenceException
Tham chiếu null không hợp lệ. OutOfMemoryException Out of memory OverflowException Lỗi tràn phép toán StackOverflowException Tràn stack TypeInitializationException
Kiểu khởi tạo sai, khi bộ khởi dựng tĩnh có lỗi.
8.4.3 Ném ngoại lệ
a. Ném lại những ngoại lệ
Khi có một ngoại lệ xảy ra chúng ta muốn phát sinh ngoại lệ ra bên ngoài khối
cacth (trong một hàm gọi) thì khi đó chúng ta cần ném ngoại lệ ra ngoài khối catch, Cú
pháp để thực hiện phát sinh lại cùng một ngoại lệ mà không có bất cứ bổ sung hay
hiệu chỉnh nào là : throw bienNgoaiLe; Ví dụ 5.4 class NgoaiLe { public int Chia() { int x, y,z=0; try {
Console.Write("Nhap gia tri x: ");
x = int.Parse(Console.ReadLine());
Console.Write("Nhap gia tri y: ");
y = int.Parse(Console.ReadLine()); z = x / y; } catch (Exception ex) {
// Ném ngoại lệ ra ngoài khối cacth
Exception x = new Exception(ex.Message); throw x; // Hoặc là
//throw new Exception(ex.Message); } return z; } } class chay { static void Main() { NgoaiLe f = new NgoaiLe(); try
{//Phương thức Chia() của lớp Ngoại lệ có thể phát sinh ngoại lệ do trong khi
xây //dựng phương thức chúng ta đã ném ngoại lệ ra ngoài khối catch.
Console.WriteLine ( f.Chia()); } catch (Exception ex) {
Console.WriteLine(ex.Message); } }
b. Tạo đối tượng ngoại lệ cho ứng dụng
CLR cung cấp những kiểu dữ liệu ngoại lệ cơ bản, trong ví dụ trước chúng ta đã
tạo một vài các kiểu ngoại lệ riêng. Thông thường chúng ta cần thiết phải cung cấp
các thông tin mở rộng cho khối catch khi một ngoại lệ được phát sinh. Tuy nhiên,
có những lúc chúng ta muốn cung cấp nhiều thông tin mở rộng hay là các khả năng
đặc biệt cần thiết trong ngoại lệ mà chúng ta tạo ra. Chúng ta dễ dàng tạo ra các ngoại
lệ riêng, hay còn gọi là các ngoại lệ tùy chọn (custom exception), điều bắt buộc với
các ngoại lệ này là chúng phải được dẫn xuất từ System.ApplicationException. Ví dụ
5.5 sau minh họa việc tạo một ngoại lệ riêng. Ví dụ 5.5
class ClsNgoaile : System.ApplicationException {
public ClsNgoaile(string message) : base(message) { } } class HinhTront { public int x, y, r;
public HinhTront(int x, int y, int r) { if (r < 0)
throw new ClsNgoaile("So Khong duoc am"); else this.x = x; this.y = y; this.r = r; } } class test { static void Main() { try {
HinhTront t = new HinhTront(4, 3, -4); } catch (ClsNgoaile ex) {
Console.WriteLine(ex.Message); } Console.ReadLine(); } }
Document Outline

  • MỤC LỤC
    • Bài 1: Các khái niệm cơ bản về lập trình hướng đối tượng
    • 1.1. Lịch sử phát triển của phương pháp lập trình
      • 1.1.1 Lập trình không có cấu trúc (hay lập trình tuyến tính)
      • 1.1.2 Lập trình hướng chức năng(lập trình cấu trúc)
      • 1.1.3 Lập trình module
      • 1.1.4 Lập trình hướng đối tượng
      • Lập trình hướng đối tượng có các đặc tính sau:
    • 1.2 Một số khái niệm trong lập trình hướng đối tượng
      • 1.2.1 Trừu tượng hóa dữ liệu (Data Abstraction)
    • Định nghĩa
    •  Những ưu điểm của việc Trừu tượng hóa:
      • 1.2.2 Lớp (Class)
    • Định nghĩa (1)
      • 1.2.3 Đối tượng (Object)
    • Hình: Trình bày hai đối tượng.
      • 1.2.4 Thuộc tính
      • 1.2.5 Hoạt động (Operation)
    • Định nghĩa:
      • 1.2.6 Phương thức (Method)
    • Định nghĩa (2)
      • 1.2.7 Thông điệp (Message)
    • Định nghĩa (3)
      • 1.2.8 Sự kiện (Event)
      • 1.2.8 Phân biệt giữa lớp và đối tượng
      • 1.2.9 Thiết lập (Construction) và Hủy (Destruction)
    • 1.2.9.1 Thiết lập
    • Định nghĩa (4)
    • 1.2.9.2 Hủy
    • Định nghĩa (5)
      • 1.2.10 Tính Bền vững (Persistence)
    • Định nghĩa (6)
      • 1.2.11 Tính Đóng gói dữ liệu
    • Định nghĩa (7)
      • 1.2.12 Tính thừa kế
      • 1.2.13 Tính Đa Thừa kế
      • 1.2.14 Tính Đa hình
    • Định nghĩa (8)
    • 1.3 Các ưu điểm của lập trình hướng đối tượng
      • 1.3.1. Các ưu điểm của lập trình hướng đối tượng
      • 1.3.2. Mốt số ngôn ngữ lập trình hướng đối tượng
      • 1.3.3. Ngôn ngữ lập trình C#
    • Bài 2: Lớp và đối tượng( 1)
    • 2.1. Lớp
      • 2.1.1. Định nghĩa lớp
      • ListBox myListBox;
      • Kết quả:
      • 2.1.2 Thành phần dữ liệu
      • [Thuộc tính] [Bổ sung truy cập] Kieudulieu Tenthanhphan;
    • Chú ý:
    • Kết quả:
      • 2.1.3 Phương thức
      • 2.1.4. Các từ khoá đặc tả truy cập
      • Chú ý:
      • 2.1.5. Khai báo đối tượng, mảng đối tượng
      • Tên_đối_tượng =new Định_danh_lớp([tham số]);
      • 2.1.6. Định nghĩa chồng phương thức
    • Chú ý: (1)
      • 2.1.7. Thuộc tính và thủ tục thuộc tính
    • Kết quả: (1)
      • Truy cập lấy dữ liệu (get accessor)
    • Bộ truy cập thiết lập dữ liệu ( set accessor)
      • 2.1.8. Từ khoá this
    • Bài 3: Lớp và đối tượng( 2)
    • 3.1. Phép gán các đối tượng
    • 3.2. Phương thức thiết lập và sao chép
      • 3.2.1. Phương thức thiết lập
    • Kết quả: (2)
      •  Sử dụng phương thức thiết lập
    • Xét ví dụ sau:
      • 3.2.2. Phương thức sao chép
    • 3.3. Huỷ bỏ đối tượng, cơ chế gom rác trong .Net
    • Bài 4: Lớp và đối tượng( 3)
    • 4.1. Thành phần tĩnh và cách sử dụng
      • 4.1.1. Thành phần dữ liệu tĩnh
      • Sử dụng các thuộc tính tĩnh
      • 4.1.2. Phương thức tĩnh
      • Gọi một phương thức tĩnh:
    • 4.2. Lớp bao và cách sử dụng
    • Bài 5: Kế thừa và đa hình(1)
    • 5.1. Giới thiệu chung về thừa kế
    • 5.2. Xây dựng lớp dẫn xuất thừa kế từ lớp cơ sở
      • Ví dụ 12.1 Xây dựng lớp cơ sở và lớp kế thừa
    • 5.3. Mức truy cập trong lớp dẫn xuất
    • 5.4. Gọi phương thức khởi tạo của lớp cơ sở
    • 5.5. Truy xuất các thành phần của lớp cơ sở
    • 5.6. Boxing và Unboxing dữ liệu
      • Boxing được thực hiện ngầm định
      • Unboxing phải được thực hiện tường minh
    • 5.7. Các lớp lồng nhau
    • Bài 6: Kế thừa và đa hình(2)
    • 6.1. Giới thiệu chung về đa hình
    • 6.2. Phương thức đa hình
      • Ví dụ: Xây dựng phương thức ảo trong lớp cơ sở, và ghi đè phương thức ảo trong lớp kế thừa
    • 6.3. Từ khoá new và override
      • Từ khóa new và override
    • Bài 7: Kế thừa và đa hình(3)
    • 7.1 Lớp trừu tượng
      • 7.1.1. Xây dựng lớp cơ sở trừu tượng
      • 7.1.2. Kế thừa từ lớp trừu tượng
      • Khi đó ta có thể sử dụng các lớp trên để khai báo như sau :
      • 7.1.3. Hạn chế của lớp trừu tượng
    • Một số chú ý:
    • 7.2. Lớp cô lập (sealed class)
    • 7.3. Lớp Object
    • Bài 8: Giao diện
    • 8.1. Giao diện
      • 8.1.1. Xây dựng giao diện
      • 8.1.2. Khai báo thuộc tính của giao diện
      • 8.1.3. Khai báo phương thức của giao diện
    • 8.2. Thực thi giao diện
      • 8.2.1. Thực thi giao diện
      • Ví dụ 4.1 (tiếp) thực thi giao diện
      • Truy cập phương thức giao diện
      • Gán đối tượng cho một giao diện
      • Giao diện đối lập với lớp trừu tượng
      • 8.2.2. Toán tử is
      • 8.2.3. Toán tử as
    • 8.3. Thực thi giao diện
      • 8.3.1. Thực thi giao diện
      • Ví dụ 4.3
      • Thực thi giao diện tường minh
      • Lựa chọn việc thể hiện phương thức giao diện
      • Ẩ n thành viên
      • 8.3.2. Mở rộng giao diện
      • Kết hợp các giao diện
      • 8.3.3. Tham chiếu đối tượng của lớp con qua giao diện
      • 8.3.4. Giao diện đối lập với lớp trừu tượng
  • 8.4 Kỹ thuật xử lý ngoại lệ
    • 8.4.1 Khái niệm xử lý ngoại lệ
      • 8.4.2. Các lớp ngoại lệ của .Net
      • 8.4.3 Ném ngoại lệ
        • Ví dụ 5.4