Bài giảng Lập trình hướng đối tượng - Chương 5: Vòng đời đối tượng và sự tương tác giữa chúng

5.0 Dẫn nhập

5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor

5.2 Quản lý ₫ời sống ₫ối tượng - Hàm Destructor

5.3 Tương tác giữa các ₫ối tượng trong VC#

5.4 Liên kết tĩnh trong việc gởi thông ₫iệp

5.5 Liên kết ₫ộng ₫ể có ₫a xạ

5.6 Xử lý sự kiện luôn có tính ₫a xạ

5.7 Kết chương

pdf 20 trang phuongnguyen 8760
Bạn đang xem tài liệu "Bài giảng Lập trình hướng đối tượng - Chương 5: Vòng đời đối tượng và sự tương tác giữa chúng", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Lập trình hướng đối tượng - Chương 5: Vòng đời đối tượng và sự tương tác giữa chúng

Bài giảng Lập trình hướng đối tượng - Chương 5: Vòng đời đối tượng và sự tương tác giữa chúng
 Chương 5
Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng
 5.0 Dẫn nhập
 5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
 5.2 Quản lý ₫ời sống ₫ối tượng - Hàm Destructor
 5.3 Tương tác giữa các ₫ối tượng trong VC#
 5.4 Liên kết tĩnh trong việc gởi thông ₫iệp
 5.5 Liên kết ₫ộng ₫ể có ₫a xạ
 5.6 Xử lý sự kiện luôn có tính ₫a xạ
 5.7 Kết chương
Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
© 2010 Slide 1
5.0 Dẫn nhập 
 Chương này giới thiệu vòng ₫ời của từng ₫ối tượng trong chương
 trình, cách thức quản lý ₫ời sống của ₫ối tượng, các thời ₫iểm
 quan trọng nhất như lúc tạo mới ₫ối tượng, lúc xóa ₫ối tượng cũng
 như cách miêu tả các hoạt ₫ộng xảy ra tại các thời ₫iểm này.
 Chương này cũng giới thiệu sự tương tác giữa các ₫ối tượng trong
 lúc chúng ₫ang sống ₫ể hoàn thành nhiệm vụ của chương trình.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 2
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
 Class mô hình các ₫ối tượng cùng loại mà phần mềm dùng. Lúc
 lập trình, ta chỉ ₫ặc tả class, ₫ối tượng chưa có. Khi ứng dụng
 chạy, tại thời ₫iểm cần thiết, phần mềm sẽ phải tạo tường minh
 ₫ối tượng bằng lệnh new :
 Rectangle objRec = new Rectangle(); //tạo ₫ối tượng
 Trạng thái của ₫ối tượng là tập giá trị cụ thể của các thuộc tính.
 Ngay sau ₫ối tượng ₫ược tạo ra, nó cần có trạng thái ban ₫ầu xác
 lập nào ₫ó. Hàm constructor cho phép người lập trình miêu tả
 hoạt ₫ộng xác lập trạng thái ban ₫ầu của ₫ối tượng.
 Cũng giống như nhiều tác vụ khác, hàm contructor có thể có nhiều
 "overloaded" khác nhau (với số lượng tham số khác nhau hay tính
 chất của 1 tham số nào ₫ó khác nhau).
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 3
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
 Mỗi lần ₫ối tượng ₫ược tạo ra (bởi lệnh new), máy sẽ gọi tự ₫ộng
 contructor của class tương ứng. Tùy theo tham số của lệnh new
 mà contructor nào tương thích sẽ ₫ược kích hoạt chạy.
 Trong nội bộ 1 class, các tác vụ chỉ có thể truy xuất các thuộc tính
 của mình và các thuộc tính thừa kế từ cha có tầm vực protected,
 public, chứ không thể truy xuất trực tiếp các thuộc tính thừa kế từ
 cha có thuộc tính private. Do ₫ó nếu chỉ chạy constructor của class
 cần tạo ₫ối tượng thì không thể khởi tạo hết các thuộc tính của ₫ối
 tượng, cần kích hoạt hết các contructor của các class cha (gián tiếp
 hay trực tiếp).
 Mặc ₫ịnh, khi cần gọi constructor của class cha chạy, máy sẽ gọi
 contructor không tham số. Nếu người lập trình muốn khác thì phải
 khai báo lại tường minh "overloaded" nào cần chạy thông qua
 mệnh ₫ề base() trong lệnh ₫ịnh nghĩa hàm contructor.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 4
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
class A {
 private int i; C c = new C(); //A()->B(3.1416)->C()
 A() { this.i = 0; }
 A(int i) { this.i = i; } 
} C = new C(true); //A()->B()->C(true)
class B : A {
 private double d;
 B() { this.d = 0; }
 B(double d) : base () { this.d = d; }
}
class C : B {
 private bool b;
 C() : base(3.1416) { this.b = false; }
 C(bool b) { this.b = b; }
}
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 5
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
//C++
for (;;) { C c = new C();
 obj = new C();
 //xử lý obj
 //xóa C = new C(true);
 delete(obj);
}
//C#
for (;;) {
 obj = new C();
 //xử lý obj
}
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 6
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
int[] vec;
vec[0] = 1; //lỗi vì phần tử vec[0] chưa có
vec = new int [10]; //tạo array gồm 10 phần tử, mỗi phần tử chứa số nguyên
vec[0] = 1; //OK
class A {public int i; }
A[] vec;
vec[0].i = 1; //lỗi vì phần tử vec[0] chưa có
vec = new A [10]; //tạo array 10 phần tử tham khảo null
vec[0].i = 1; //lỗi vì phần tử vec[0] chứa tham khảo null
vec[0] = new A();
vec[0].i = 1; //OK
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 7
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
//class A có 2 hàm contructor
class A {
 A() {...}
 A(int i) {...}
 ...
};
//class B thừa kế A, có 2 hàm contructor
class B : A {
 B() : base() {...}
 B(int i) : base (i) {...}
 ...
};
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 8
5.1 Quản lý ₫ời sống ₫ối tượng - Hàm Constructor
//class C thừa kế B, có 2 hàm contructor
class C : B {
 C() : base () {...}
 C(int i) : base (i) {...}
 ... };
C c1 = new C(); //kích hoạt A() B() C()
C c2 = new C(5); //kích hoạt A(5) B(5) C(5)
 Việc xác ₫ịnh contructor nào ₫ược kích hoạt phải theo chiều từ
 dưới lên bắt ₫ầu từ class ₫ược new, nhưng các contructor ₫ược
 chạy thực sự sẽ theo chiều từ trên xuống bắt ₫ầu từ class tổ tiên
 ₫ời ₫ầu.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 9
5.2 Quản lý ₫ời sống ₫ối tượng - Hàm Destructor
 Đối tượng là 1 thực thể, nó có ₫ời sống như bao thực thể khác.
 Như ta ₫ã biết, khi ta gọi lệnh new, 1 ₫ối tượng mới thuộc class
 tương ứng sẽ ₫ược tạo ra (trong không gian hệ thống), trạng thái
 ban ₫ầu sẽ ₫ược xác lập thông qua việc kích hoạt dây chuyền các
 contructor của các class thừa kế. Chương trình sẽ lưu giữ tham
 khảo ₫ến ₫ối tượng trong biến tham khảo ₫ể khi cần, gởi thông
 ₫iệp nhờ ₫ối tượng thực thi dùm 1 tác vụ nào ₫ó.
 VC# không cung cấp tác vụ delete ₫ể xóa ₫ối tượng khi không cần
 dùng nó nữa. Thật vậy, ₫ánh giá 1 ₫ối tượng nào ₫ó có cần dùng
 nữa hay không là việc không dễ dàng, dễ nhằm lẫn nếu ₫ể chương
 trình tự làm.
 Tóm lại, trong VC#, chương trình chỉ tạo tường minh ₫ối tượng
 khi cần dùng nó, chương trình không quan tâm việc xóa ₫ối tượng
 và cũng không có khả năng xóa ₫ối tượng.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 10
5.2 Quản lý ₫ời sống ₫ối tượng - Hàm Destructor
 Như vậy, ₫ối tượng sẽ bị xóa lúc nào, bởi ai ? Hệ thống có 1
 module ₫ặc biệt tên là "Garbage collection" (trình dọn rác),
 module này sẽ theo dõi việc dùng các ₫ối tượng, khi thấy ₫ối
 tượng nào mà không còn ai dùng nữa thì nó sẽ xóa dùm tự ₫ộng.
 Trình dọn rác không biết trạng thái ₫ối tượng tại thời ₫iểm bị xóa
 nên nó không làm gì ngoài việc thu hồi vùng nhớ mà ₫ối tượng
 chiếm. Như vậy rất nguy hiểm, thí dụ như ₫ối tượng bị xóa ₫ã mở,
 khóa file và ₫ang truy xuất file dỡ dang.
 Để giải quyết vấn ₫ề xóa ₫ối tượng ₫ược triệt ₫ể, trình dọn rác sẽ
 gọi tác vụ destructor của ₫ối tượng sắp bị xóa, nhiệm vụ của người
 ₫ặc tả class là hiện thực tác vụ này.
 Tác vụ destructor không có kiểu trả về, không có tham số hình
 thức không có overloaded, chỉ có 1 destructor/class mà thôi.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 11
5.2 Quản lý ₫ời sống ₫ối tượng - Hàm Destructor
 Mặc dù người ₫ặc tả class sẽ hiện thực tác vụ destructor nếu thấy
 cần thiết, nhưng code của chương trình không ₫ược gọi trực tiếp
 destructor của ₫ối tượng. Chỉ có trình dọn rác của hệ thống mới
 gọi destructor của ₫ối tượng ngay trước khi xóa ₫ối tượng ₫ó.
 Destructor của 1 class cũng chỉ xử lý trạng thái ₫ối tượng do các
 thuộc tính của class ₫ó qui ₫ịnh, nó cần gọi destructor của class
 cha ₫ể xử lý tiếp trạng thái ₫ối tượng do các thuộc tính private của
 class cha qui ₫ịnh, và cứ thế tiếp tục.
 Tóm lại trước khi xóa một ₫ối tượng, trình dọn rác sẽ gọi các
 destructor theo chiều từ dưới lên, bắt ₫ầu từ class hiện hành của
 ₫ối tượng, sau ₫ó tới class cha, ... và cuối cùng là class tổ tiên ₫ời
 ₫ầu (root).
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 12
5.3 Tương tác giữa các ₫ối tượng trong VC#
 Muốn tương tác với ₫ối tượng nào ₫ó, ta phải có tham khảo ₫ến
 ₫ối tượng ₫ó. Thường ta lưu giữ tham khảo ₫ối tượng cần truy
 xuất trong biến ₫ối tượng (biến tham khảo). Thông qua tham khảo
 ₫ến ₫ối tượng, ta có thể thực hiện 1 trong các hành ₫ộng tương tác
 sau ₫ây :
 1. truy xuất 1 thuộc tính vật lý của ₫ối tượng có tầm vực cho phép
 (public hay internal hay protected).
 2. truy xuất 1 thuộc tính luận lý của ₫ối tượng.
 3. gọi 1 tác vụ hay toán tử có tầm vực cho phép.
 4. truy xuất 1 event của ₫ối tượng.
 5. truy xuất 1 phần tử trong danh sách indexer của ₫ối tượng.
 Gọi 1 tác vụ hay 1 toán tử là giống nhau và cần làm rõ chi tiết
 trong các slide sau.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 13
5.4 Liên kết tĩnh trong việc gởi thông ₫iệp
 Xét ₫oạn lệnh sau :
class C1 {
 public void func1() {}//dịch ra hàm mã máy có tên là C1_func1
 public virtual func2() {} //dịch ra hàm mã máy có tên là C1_func2
}
class C2 : C1 {
 public override void func1() {} //dịch ra hàm mã máy có tên là C2_func1
 public override func2() {} //dịch ra hàm mã máy có tên là C2_func2
}
C1 obj = new C1();
obj.func1(); //lần 1 gọi hàm mã máy nào ?
//₫oạn code có thể làm obj chỉ về ₫ối tượng của class C2, C3,...
obj.func1(); //lần 2 gọi hàm mã máy nào ?
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 14
5.4 Liên kết tĩnh trong việc gởi thông ₫iệp
Hai lệnh gởi thông ₫iệp obj.func1() trong slide trước sẽ kích hoạt tác
vụ func1() của class C1 hay tác vụ func1() của class C2 ?
 1. Dùng kỹ thuật xác ₫ịnh hàm và liên kết tĩnh : Tại thời ₫iểm
 dịch, chương trình dịch chỉ biết biến obj thuộc kiểu C1 và nó dịch
 cả 2 lời gởi thông ₫iệp obj.func1() thành lời gọi hàm C1_func1().
 Như vậy mỗi khi máy chạy lệnh obj.func1() lần 1, hàm
 C1_func1() sẽ ₫ược gọi, ₫iều này ₫úng theo yêu cầu của phần
 mềm. Nhưng khi máy chạy lệnh obj.func1() lần 2, hàm
 C1_func1() cũng sẽ ₫ược gọi, ₫iều này không ₫úng theo yêu cầu
 của phần mềm vì lúc này obj ₫ang tham khảo ₫ối tượng của class
 C2.
 Mặc ₫ịnh, VC# dùng kỹ thuật xác ₫ịnh hàm và liên kết tĩnh khi
 dịch lời gởi thông ₫iệp, do ₫ó tạo ra ₫ộ rủi ro cao, chương trình
 ứng dụng thường chạy không ₫úng theo yêu cầu mong muốn!!!
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 15
5.5 Liên kết ₫ộng ₫ể ₫ảm bảo tính ₫a xạ
Bây giờ nếu ta hiệu chỉnh 2 lệnh gởi thông ₫iệp obj.func1() trong
slide trước thành obj.func2() thì máy sẽ kích hoạt tác vụ func2() của
class C1 hay tác vụ func2() của class C2 ?
 2. Dùng kỹ thuật xác ₫ịnh hàm và liên kết ₫ộng :Lệnhgởithông
 ₫iệp obj.func2() ₫ược dịch thành ₫oạn lệnh máy với chức năng
 sau : xác ₫ịnh biến obj ₫ang tham khảo ₫ến ₫ối tượng nào, thuộc
 class nào, rồi gọi hàm func2() của class ₫ó chạy. Như vậy, lần gởi
 thông ₫iệp 1, biến obj ₫ang tham khảo ₫ối tượng thuộc class C1
 nên máy sẽ gọi hàm C1_func2(), ₫iều này ₫úng theo yêu cầu của
 phần mềm. Khi máy chạy lệnh obj.func2() lần 2, ₫oạn code xác
 ₫ịnh hàm và liên kết ₫ộng sẽ gọi ₫ược hàm C2_func2(), ₫iều này
 cũng ₫úng theo yêu cầu của phần mềm. Ta nói lời gởi thông ₫iệp
 obj.func2() có tính ₫a xạ.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 16
5.5 Liên kết ₫ộng ₫ể ₫ảm bảo tính ₫a xạ
 Trong VC#, nếu dùng từ khóa virtual tronglệnh₫ịnhnghĩatácvụ
 thì tác vụ này sẽ ₫ược xử lý theo cơ chế liên kết ₫ộng và sẽ ₫ảm bảo
 ₫ược tính ₫a xạ, tức ₫ảm bảo tính ₫úng ₫ắn trong lời gởi thông
 ₫iệp. Biết ₫ược ₫iều này, từ ₫ây về sau, mỗi lần ₫ịnh nghĩa 1 tác vụ
 hay 1 toán tử, ta hãy luôn dùng từ khóa virtual kết hợp với nó.
 Lưu ý rằng 2 tác vụ constructor và destructor của ₫ối tượng là 2 tác
 vụ ₫ặc biệt, chúng quản lý ₫ời sống ₫ối tượng và chỉ ₫ược gọi bởi
 hệ thống. Ta không ₫ược phép dùng từ khóa virtual khi ₫ịnh nghĩa
 chúng.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 17
5.6 Xử lý sự kiện luôn có tính ₫a xạ
 Chúng ta hãy viết 1 chương trình nhỏ gồm 1 form giao diện, trong
 form ta tạo 1 Button có thuộc tính Text="Làm gì ₫ây?", thuộc tính
 (Name) = btnStart, ₫ịnh nghĩa hàm xử lý sự kiện Click cho nó rồi
 viết code như sau :
 //hàm xử lý Click chuột trên button do máy tạo ra
 private void btnStart_Click(object sender, EventArgs e) {
 //xuất thông báo ₫ể kiểm tra
 MessageBox.Show("Hàm btnStart_Click sẽ xứ lý ₫ây");
 //thay ₫ổi hàm xử lý Click cho Button
 this.btnStart.Click -= new EventHandler(btnStart_Click);
 this.btnStart.Click += new EventHandler(btnStart_Click1);
 }
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 18
5.6 Xử lý sự kiện luôn có tính ₫a xạ
 Hãy viết thêm hàm btnStart_Click1() như sau :
 //hàm xử lý Click chuột trên button tự viết thêm
 private void btnStart_Click1(object sender, EventArgs e) {
 //xuất thông báo ₫ể kiểm tra
 MessageBox.Show("Hàm btnStart_Click1 sẽ xứ lý ₫ây");
 //thay ₫ổi hàm xử lý Click cho Button
 this.btnStart.Click -= new EventHandler(btnStart_Click1);
 this.btnStart.Click += new EventHandler(btnStart_Click);
 }
 Bây giờ nếu chạy chương trình, lần ₫ầu click chuột ta sẽ thấy hàm
 btnStart_Click() chạy, nhưng nếu click chuột tiếp thì hàm
 btnStart_Click1() chạy, cứ thế thay phiên nhau chạy (theo ý muốn
 người lập trình). Ta nói xử lý sự kiện người dùng luôn có tính ₫a
 xạ.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 19
5.7 Kết chương 
 Chương này ₫ã giới thiệu vòng ₫ời của từng ₫ối tượng trong
 chương trình, cách thức quản lý ₫ời sống của ₫ối tượng, các thời
 ₫iểm quan trọng nhất như lúc tạo mới ₫ối tượng, lúc xóa ₫ối tượng
 cũng như cách miêu tả các hoạt ₫ộng xảy ra tại các thời ₫iểm này.
 Chương này cũng ₫ã giới thiệu sự tương tác giữa các ₫ối tượng
 trong lúc chúng ₫ang sống ₫ể hoàn thành nhiệm vụ của chương
 trình. Gởi thông ₫iệp là sự tương tác chính yếu giữa các ₫ối tượng,
 và cần phải có tính ₫a xạ.
 Khoa Khoa học & Kỹ thuật Máy tính Môn : Lập trình hướng ₫ối tượng
 Trường ĐH Bách Khoa Tp.HCM Chương 5 : Vòng ₫ời ₫ối tượng và sự tương tác giữa chúng trong C#
 © 2010 Slide 20

File đính kèm:

  • pdfbai_giang_lap_trinh_huong_doi_tuong_chuong_5_vong_doi_doi_tu.pdf