Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Chương 3: Lập trình hướng đối tượng trong C#
3.1 Xây dựng lớp – Đối tượng
3.1.1 Định nghĩa lớp
Trong thế giới thực, bạn thường thấy nhiều đối tượng riêng lẻ thuộc cùng một loại. Có thể có hàng nghìn chiếc xe đạp cùng tồn tại, tất cả chúng đều giống nhau về
cách sản xuất và mẫu mã. Mỗi chiếc xe đạp đã được tạo ra từ một tập thiết kế chung,
vì thế chúng giống nhau về thành phần cấu tạo. Trong thuật ngữ hướng đối tượng,
chúng ta nói rằng chiếc xe đạp là một thể hiện của một lớp các đối tượng có tên gọi là xe đạp. Một lớp là một bản thiết kế mà từ đó các đối tượng cụ thể được tạo ra.
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 trước, các hàm đề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 classTester
{
public static int Main()
{
//....
}
} Để đị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:
[Mức độ truy cập] class <Định danh lớp> [: Lớp cơ sở]
{
[Nội dung lớp có thể bao gồm một hoặc nhiều thuộc tính và phương
thức]
[Mức độ truy cập] <Định danh thuộc tính>;
[Nội dung phương thức]
[Mức độ truy cập] <Định danh phương thức>() { }
}
Trong đó:
46 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Mức độ truy cập: Thông thường, mức độ truy cập (access-modifiers) của một
lớp là public. Ngoài ra các thành phần của lớp cũng có mức độ truy cập riêng cho biết
loại phương thức nào được phép truy cập đến nó, hay nói cách khác nó mô tả phạm vi
mà thành phần đó được nhìn thấy.
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ì các phương thức của lớp bao gồm
những 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 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 những
phương thức của bất cứ lớp nào trong
cùng khối hợp ngữ với A.
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 A, và bất cứ
lớp nào trong cùng khối hợp ngữ của A.
Định danh: Đị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. Các định danh cần phải được đặt tên theo đúng quy tắc (Chỉ có thể chứa kí
tự số, chữ và “_”, phân biệt chữ hoa, chữ thường và không được bắt đầu bằng số).
Lớp cơ sở: Lớp cơ sở là lớp mà đối tượng sẽ kế thừa để phát triển ta sẽ bàn sau. Thuộc tính (trường dữ liệu): là biến dữ liệu của lớp đối tượng.
Ví dụ:
public int tuoi;//Khai báo thuộc tính tuổi cho lớp nguoi với kiểu int
public class nguoi { }
Phương thức (Methods): là thành phần xử lý của lớp đối tượng. Khi khai báo và
định nghĩa 1 phương thức cần xác định những thành phần sau đây:
47 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
- Kiểu trả về: một phương thức có thể trả về một kiểu dữ liệu có sẵn hoặc do
người dùng định nghĩa hoặc không trả về gì cả (void).
- Tên phương thức: một định danh tuân thủ nguyên tắc đặt tên.
- Danh sách tham số: một phương thức có thể có tham số truyền vào hoặc không o Truyền tham số theo kiểu tham trị (mặc định): giá trị của các biến trước khi được truyền vào phương thức và sau khi ra khỏi phương thức không thay đổi.
Ví dụ:
//Khai báo phương thức int ttong (int a, int b)
{
a=a+5;
b=b+1; return a+b;
}
//Gọi hàm truyền tham số
int so1=5, so2=10, tong;
tong=ttong(so1,so2);
Quan sát ví dụ trên ta thấy ban đầu so1 có giá trị là 5, so2 có giá trị là 10 khi
được truyền vào trong phương thức ta thấy so1 sẽ được cộng thêm 5 và so2 được cộng thêm 1. Nhưng sau khi gọi hàm xong giá trị của so1 vẫn là 5 và so2
vẫn là 10.
o Truyền tham số theo kiểu tham chiếu với từ khóa ref và out : là kiểu truyền tham số vào phương thức mà trong phương thức có thay đổi giá trị của biến thì
sau khi ra khỏi phương thức giá trị của biến sẽ thay đổi. Điều nay tỏ ra rất hữu
ích khi ta muốn một phương thích trả về nhiều hơn một giá trị.
Ví dụ:
//Khai báo phương thức
a=a+5; b=b+1; return a+b;
int ttong (ref int a, ref int b) { } //Gọi hàm truyền tham số
int so1=5, so2=10, tong;
tong=ttong(so1,so2);
48 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Kết quả sau khi ra khỏi phương thức ttong so1 sẽ có giá trị bằng 10, và so2 có
giá trị là 11. Từ khóa ref và out đều có thể được sử dụng trong trường hợp này,
sự khác nhau cơ bản là nếu dùng out thì không cần khởi tạo các biến trước khi
truyền vào phương thức.
o Truyền tham số kiểu mảng với từ khóa params : điều này rất hữu ích khi ta muốn tạo ra một phương thức với số lượng tham số là một dãy số, với số
lượng không có định, tùy thuộc vào lúc gọi phương thức.
Ví dụ: //Khai báo phương thức
public void DisplayVals( params int[] intVals)
foreach (int i inintVals)
{ Console.WriteLine(“DisplayVals: {0}”, i);
}
//Gọi phương thức
DisplayVals(1,2,6,5,4,7);
DisplayVals(2,2,1,3,4,3,5,6,6,8,9);
int[] mang = new int[5]{2,4,9,7,8};
DisplayVals(mang);
- Phần thực thi của phương thức: nội dung phương thức tùy vào mục đích sử
dụng của phương thức.
Ví dụ.
public class nguoi
{
public void hello()// Phương thức không có tham số, không trả về gì cả
{
Console.WriteLine(“Xin chao cac ban”);
} public int tinhTuoi(int nam_sinh)// Phương thức có tham số và trả về { int tuoi=2014-nam_sinh; return tuoi;//Với phương thức có giá trị trả về cần có câu lệnh
kiểu int return để xác định xem phương thức sẽ trả về cái gì. }
}
3.1.2 Tạo đối tượng
49 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Các đối tượng là điểm cốt lõi để hiểu về công nghệ hướng đối tượng. Bây giờ
hãy nhìn xung quanh và bạn sẽ thấy được rất nhiều ví dụ về đối tượng của thế giới
thực: con chó, cái bàn, ti vi, xe đạp.
Đối tượng trong thế giới thực có chung hai đặc điểm: Tất cả đều có trạng thái và hành vi. Chó có trạng thái (tên, màu sắc, loại, tình trạng đói hay no) và hành vi
(sủa, tha đồ vật đến, vẫy đuôi). Xe đạp cũng có trạng thái (bánh răng, nhịp bàn đạp
hiện tại, tốc độ hiện tại) và hành vi (thay đổi bánh răng, thay đổi nhịp bàn đạp, sử dụng
phanh). Việc xác định trạng thái và hành vi của các đối tượng trong thế giới thực là một cách tuyệt vời để bắt đầu nghĩ đến các khái niệm của lập trình hướng đối tượng.
Bây giờ ta hãy dành ra một vài phút để quan sát các đối tượng của thế giới thực
xung quanh bạn. Đối với mỗi đối tượng mà bạn nhìn thấy, hãy tự đặt ra cho mình hai
câu hỏi: “Đối tượng này có thể ở trong những trạng thái nào?” và “Đối tượng này có thể thưc hiện những hành vi nào?” Hãy chắc chắn rằng bạn đã ghi lại những quan sát
của mình. Khi làm thế, bạn sẽ nhận thấy rằng các đối tượng trong thế giới thực có sự
khác nhau về độ phức tạp; chiếc đèn bàn có thể chỉ có 2 trạng thái (đang bật và đang
tắt) và hai hành vi (bật và tắt), nhưng chiếc radio có thể có thêm các trạng thái khác
(đang bật, đang tắt, âm lượng hiện tại, kênh hiện tại) và hành vi (bật, tắt, tăng âm
lượng, giảm âm lượng, tìm kiếm, dò kênh và điều chỉnh). Bạn cũng sẽ nhận thấy rằng
một số đối tượng này có thể chứa các đối tượng khác. Những quan sát về thế giới thực này đều được chuyển vào trong thế giới của lập trình hướng đối tượng.
Một đối tượng phần mềm.
Về mặt khái niệm thì các đối tượng phần mềm cũng tương tự như các đối tượng trong thế giới thực: Nó cũng bao gồm các trạng thái và hành vi liên quan. Một đối tượng lưu trữ trạng thái của nó trong các trường (có thể được gọi là biến trong một số ngôn ngữ lập trình) và thể hiện các hành vi của mình ra bên ngoài thông qua các
phương thức (có thể được gọi là hàm ở trong một số ngôn ngữ lập trình). Các phương
thức thao tác trên các trạng thái bên trong của một đối tượng và được dùng như là cơ
50 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
chế chính cho sự giao tiếp giữa đối tượng với đối tượng. Việc ẩn đi trạng thái bên
trong và bắt buộc tất các các tương tác đều phải được thực hiện thông qua các phương
thức của một đối tượng được biết đến như là sự bao gói dữ liệu (data encapsulation) –
một nguyên lý cơ bản của lập trình hướng đối tượng. Hãy lấy một chiếc xe đạp làm ví dụ:
Chiếc xe đạp được mô hình hóa như là một đối tượng phần mềm.
Bằng cách mô tả các trạng thái (tốc độ hiện tại, nhịp đạp hiện tại, bánh răng hiện tại)
và cung cấp các phương thức để thay đổi các trạng thái đó, thì một đối tượng vẫn kiểm
soát được cách thức mà thế giới bên ngoài được phép sử dụng nó. Ví dụ, nếu chiếc xe
đạp chỉ có 6 bánh răng, một phương thức để thay đổi bánh răng có thể từ chối bất cứ
giá trị nào nhỏ hơn 1 hoặc lớn hơn 6.
Cú pháp tạo đối tượng
Ví dụ:
//Khai báo lớp human
class human()
{
int namSinh;
Console.WriteLine(“Xin chao cac ban”);
string ten; void chao() { }
} //Tạo một đối tượng thuộc lớp human human hm1=new human(); //Đối tượng hm1 sẽ có 2 thuộc tính là namSinh và ten cũng như phương
Phương thức khởi tạo (Constructor)
51 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Constructor được định nghĩa trong quá trình khai báo class, nếu như không định
nghĩa Constructor thì chương trình sẽ tự sinh Constructor mặc định không chứa đối số.
Mục đích chính của Constructor là thiết lập các giá trị cho các thuộc tính sau khi khởi
tạo các đối tượng. Constructor có thể có một, nhiều đối số hoặc không có đối số nào. Tên phương thức Constructor khi báo phải trùng (chính xác) tên với tên của class.
Có thể tạo nhiều Constructor cho class nhưng các constructor phải khác nhau về
đối số. Đây cũng là khái niệm overload trong lập trình hướng đối tượng, nghĩa là tạo
nhiều hàm / phương thức cùng tên nhưng khác về đối số. Đối số khác nhau ở đây là số lượng đối số và thứ tự các đối số. Constructor không có đối số là constructor mặc định.
Hiểu nôm na là phương thức khởi dựng sẽ là phương thức được thực hiện đầu tiên khi
vừa tạo đối tượng.
Ví dụ
//Khai báo lớp
class Library
{
private int ibooktypes;
//Constructor
public Library() // Hàm tạo không đối số. {
ibooktypes = 7;
}
public Library(int value) // Hàm tạo có một đối số
{
ibooktypes = value;
}
}
//Tạo đối tượng Library lb1 = new Library(); // Hàm tạo không đối số; Library lb2 = new Library(100); // Hàm tạo được truyền vào với giá trị là 100, và nó sẽ được gán vào ibooktypes = 100;
Khởi tạo biến thành viên
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:
Ví dụ:
52 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
private int Giay = 30; // Khởitạ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.
Sử dụng từ khóa this
Từ khóa this được dùng để tham chiếu đến thể hiện hiện hành của một đối
tượng. Tham chiếu this này được xem là con trỏ ẩn đến tất các phương thức không có thuộc tính tĩnh trong một lớp. Mỗi phương thức có thể tham chiếu đến những phương
thức khác và các biến thành viên thông qua tham chiếu this này.
Ví dụ:
//Khai báo lớp class Library
{
private int ibooktypes;
//Constructor
public Library() // Hàm tạo không đối số.
{
ibooktypes = 7; }
public Library(int ibooktypes ) // Hàm tạo có một đối số
{
this.ibooktypes = ibooktypes; //Sử dụng từ khóa this để tham chiếu tới
thành phần của lớp
}
}
3.1.3 Sử dụng các thành viên static
Việc truy cập đến thành viên tĩnh phải thực hiện thông qua tên lớp (không được
truy cập thành viên tĩnh thông qua đối tượng) theo cú pháp chung:
TênLớp.TênThànhViênTĩnh
- Sử dụng phương thức tĩnh
Một phương thức static có phạm vi hoạt động giống như một phương thức toàn cục mà không cần tạo ra bất cứ một thể hiện nào của lớp cả. Toàn cục ở đây hiểu theo nghĩa là toàn cục trong lớp. Để truy cập tới các thành viên tĩnh ta có cú pháp như sau:
Tenlop.tenhamtinh ([danh sach tham so neu co]:
Ví dụ diem.hien() là lời gọi đến phương thúc tĩnh có tên là hien() của lớp diem.
- Sử dụng các thuộc tính tĩnh:
53 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Trong C# không hề có một biến nào có phạm vi hoạt động toàn cục như trong
một số ngôn ngữ lập trình khác ( pascal, C, C++, Visual Basic …) việc sử dụng
một biến với mục đích “toàn cục” trở nên là một điều không thể. Biến toàn cục
trong các ngôn ngữ khác được hiểu là toàn cục trong ứng dụng nhưng đối với C# thì toàn cục theo nghĩa hiểu của nó là toàn cục trong một lớp và không có
khái niệm toàn cục trong toàn bộ chương trình. Nếu ta khai báo một biến thành
viên tĩnh của lớp thì biến thành viên tĩnh này có tầm vực hoạt động theo ý nghĩa
toàn cục đó. Các biến thành viên tĩnh có hoạt động tích cực trong vai trò này. Cú pháp sử dụng thuộc tính tĩnh:
Tenlop.tenThuocTinh;
3.1.4 Nạp chồng Thông thường khi lập trình thì các phương thức, các biến sẽ không được có tên trùng nhau. Tuy nhiên trong C# lại có cơ chế cho phép chúng ta đặt tên các phương
thức trùng nhau nhưng phải khác nhau về thành phần tham số, điều này goi là nạp
chồng phương thức.
Ví dụ:
void myMethod( int p1 );
void myMethod( float p1 );
void myMethod( int p1, int p2 ); void myMethod( int p1, string p2 );
trong ví dụ trên ta thấy có 4 phương thức trùng tên nhau tuy nhiên lại khác nhau
về thành phần tham số (số lượng tham số, kiểu dữ liệu của tham số…)
3.2 Kế thừa - Đa hình
3.2.1 Sự kế thừa
Là ngôn ngữ lập trình hướng đối tượng, nên C# cũng có đặc điểm về thừa kế
(inheritance). Thừa kế là một trong những tính chất của lập trình hướng đối tượng.
Nhờ sử dụng khái niệm thừa kế mà ta có thể tạo ra các lớp mới từ lớp cơ sở đã có. Khi
sử dụng thừa kế, ta có thể tái sử dụng mã chương trình.
Một số khái niệm cơ bản
Lớp thừa kế gọi là lớp dẫn xuất (derived class) hay lớp con. Lớp cơ sở (base class) còn gọi là lớp cha. Lớp dẫn xuất thừa kế các thuộc tính (dữ liệu), các phương thức (method) của
lớp cơ sở (base class).
Cú pháp khai báo kế thừa
[mức độ truy cập] class
{ }
54 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Khi khai báo như trên tức là lớp con kế thừa lớp cha, lớp con sẽ được kế thừa
các thuộc tính và phương thức của lớp cha tùy thuộc vào thuộc mức độ truy cập
của các thành phần đó.
Ví dụ:
public class lopcha
{
int a=10;
public void hello() {
Console.WriteLine("Day la phuong thuc cua lop cha");
}
} public class lopcon:lopcha//lopcon được khai báo kế thừa lopcha
{
//lớp con sẽ được sử dụng các thành phần của lớp cha
}
static void Main(string[] args)
{
lopcon lc = new lopcon(); lc.hello();//Vì phương thức hello của lớp cha khai báo public nên lớp con được
sử dụng
//lc.a = 10;//Vì thuộc tính a không khai báo mức độ truy cập (mặc định sẽ là
private) nên lớp con không thể sử dụng được.
Console.ReadKey();
}
Kết quả: Ta thấy lớp con không có phương thức nào nhưng do kế thừa lớp cha
nên nó sử dụng phương thức hello của lớp cha.
Gọi phương thức khởi dựng của lớp cơ sở Vì lớp dẫn xuất không thể kế thừa phương thức khởi dựng của lớp cơ sở nên một lớp dẫn xuất phải thực thi phương thức khởi dựng riêng của mình. Nếu lớp cơ sở có một phương thức khởi dựng mặc định (tức là không có phương thức khởi dựng hoặc phương thức khởi dựng không có tham số) thì phương thức khởi dựng của lớp dẫn xuất được định nghĩa như cách thông thường. Nếu lớp cơ sở có phương thức khởi dựng có tham số thì lớp dẫn xuất cũng phải định nghĩa phương thức khởi dựng có
tham số theo cú pháp sau:
TênLớpCon(ThamSốLớpCon): TênLớpCơSở(ThamSốLớpCha)
55 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
{
// Khởi tạo giá trị cho các thành phần của lớp dẫn xuất
}
Ví dụ:
//Lop co so class Point2D
{
public int x,y; //phuong thuc tao lap cua lop co so co tham so public Point2D(int a,
int b)
{
x = a; y = b;
}
public void Xuat2D()
{
Console.Write("({0}, {1})", x, y);
}
}
//Lop dan xuat
class Point3D:Point2D
{
public int z;
//Vi phuong thuc tao lap cua lop co so co tham so nen phuong thuc tao lap cua
lop dan xuat cung phai co tham so
public Point3D(int a, int b, int c):base (a,b) {
z = c;
} public void Xuat3D() {
Console.Write("({0}, {1}, {2})", x, y, z);
}
56 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
}
class PointApp
{
public static void Main()
{
Point2D p2 = new Point2D(1, 2); Console.Write("Toa do
cua diem 2 D :"); p2.Xuat2D();
Console.WriteLine();
Point3D p3 = new Point3D(4,5,6); Console.Write("Toa do
cua diem 3 D :"); p3.Xuat3D();
Console.ReadLine();
}
}
Trong ví dụ trên, vì phương thức khởi dựng của lớp Point2D có tham số:
public Point2D(int a, int b ) nên khi lớp Point3D dẫn xuất từ lớp Point2D, phương thức khởi dựng của nó cần có ba tham số. Hai tham số đầu tiên dùng để khởi gán cho
các biến x, y kế thừa từ lớp Point2D, tham số còn lại dùng để khởi gán cho biến thành
viên z của lớp Point3D. Phương thức khởi dựng có nguyên mẫu như sau:
public Point3D(int a, int b, int c):base (a, b)
Phương thức khởi dựng này gọi phương thức khởi dựng của lớp cơ sở Point2D
bằng cách đặt dấu “:” sau danh sách tham số và từ khoá base với các đối số tương ứng
với phương thức khởi dựng của lớp cơ sở.
Truy xuất tới các thành phần lớp cơ sở Trong lớp dẫn xuất để truy xuất tới các thành phần thuộc lớp cơ sở ta sử dụng
từ khóa base.
base.tenThanhPhan; Trong ví dụ trên lớp Point3 kế thừa lớp Point2 vậy trong lớp Point3 ta có thể gọi tới phương thức Xuat2D() trong lớp Point2 bằng câu lệnh: base.Xuat2D(); nếu muốn gọi tới các thuộc tính của lớp cơ sở ta cũng làm tương tự.
Định nghĩa phiên bản mới trong lớp dẫn xuất Lớp dẫn xuất sẽ kế thừa tất cả các thành viên của lớp cơ sở, bao gồm tất cả các phương thức và biến thành viên của lớp cơ sở. Lớp dẫn xuất được tự do thực thi các
57 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
phiên bản của một phương thức của lớp cơ sở. Tuy nhiên trong một số trường hợp lớp
dẫn xuất muốn tạo ra các phiên bản mới cho một số phương thức của lớp cơ sở cho
phù hợp với mình. Lớp dẫn xuất có thể làm việc đó thông qua từ khóa new.
Ví dụ:
class XeHoi
{
protected int TocDo; protected string BienSo;
protected string HangSX;
public XeHoi(int td, string BS, string HSX)
{
TocDo = td; BienSo = BS; HangSX = HSX;
}
public void Xuat()
{
Console.Write("Xe: {0}, Bien so: {1}, Toc do: {2} kmh",HangSX, BienSo,
TocDo);
} }
class XeCar: XeHoi
{
int SoHanhKhach;
public XeCar(int td, string BS, string HSX, int SHK): base(td, BS, HSX)
{
SoHanhKhach = SHK;
} public new void Xuat()//Lớp XeCar kế thừa lớp XeHoi và tiến hành định nghĩa một
phiên bản mới cho phương thức Xuat() với từ khóa new { // Goi phuong thuc xuat cua lop co so thong qua tu khoa base
base.Xuat(); Console.WriteLine(", {0} cho ngoi", SoHanhKhach);
}
}
3.2.2 Đa hình
58 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Hai đặc điểm mạnh nhất của kế thừa đó là khả năng sử dụng lại mã chương
trình và đa hình (polymorphism). Đa hình là ý tưởng “sử dụng một giao diện chung
cho nhiều phương thức khác nhau”, dựa trên phương thức ảo (virtual method) và cơ
chế liên kết muộn (late binding). Nói cách khác, đây là cơ chế cho phép gởi một loại thông điệp tới nhiều đối tượng khác nhau mà mỗi đối tượng lại có cách xử lý riêng
theo ngữ cảnh tương ứng của chúng.
Tính đa hình (polymorphism) đượ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. Ví dụ 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.
Để thực hiện được đa hình ta phải thực hiện các bước sau:
1. Lớp cơ sở đánh dấu phương thức ảo bằng từ khóa virtual hoặc abstract. 2. Các lớp dẫn xuất định nghĩa lại phương thức ảo này (đánh dấu bằng từ khóa
override).
3. Vì tham chiếu thuộc lớp cơ sở có thể trỏ đến một đối tượng thuộc lớp dẫn xuất và có thể truy cập hàm ảo đã định nghĩa lại trong lớp dẫn xuất nên khi thực thi
chương trình, tùy đối tượng được tham chiếu này trỏ tới mà phương thức tương
ứng được gọi thực hiện. Nếu tham chiếu này trỏ tới đối tượng thuộc lớp cơ sở
thì phương thức ảo của lớp cơ sở được thực hiện. Nếu tham chiếu này trỏ tới đối
tượng thuộc lớp dẫn xuất thì phương thức ảo đã được lớp dẫn xuất định nghĩa
lại được thực hiện.
Ví dụ:
using System; public class MyWindow {
protected int top, left; //Toa do goc tren ben trai public MyWindow(int t, int l) {
top = t; left = l;
} // Phuong thuc ao
59 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
public virtual void DrawWindow( )
{
Console.WriteLine("...dang ve Window tai toa do {0}, {1}", top,
left);
}
}
public class MyListBox : MyWindow
{
string listBoxContents;
public MyListBox(int top,int left,string contents):base(top, left)
{
listBoxContents = contents; }
public override void DrawWindow( )
{
Console.WriteLine("...dang ve listbox {0} tai toa do: {1},{2}",
listBoxContents, top, left);
}
} public class MyButton : MyWindow
{
public MyButton(int top,int left):base(top, left) {}
public override voidDrawWindow( )
{
Console.WriteLine ("...dang ve button tai toa do:{0},{1}", top, left);
}
}
public class Tester {
static void Main( ) {
Random R = new Random(); int t; string s = "";
MyWindow[] winArray = new MyWindow[4];
for(int i = 0;i < 4; i++) {
60 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
t = R.Next() % 3;
switch(t)
{
case 0: winArray[i] =new MyWindow( i * 2, i * 4); break;
case 1:
s = "thu " + (i+1).ToString();
winArray[i] = new MyListBox(i*3, i * 5, s); break;
case 2: winArray[i] =new MyButton(i * 10, i * 20);
break;
} }
for(int i = 0; i < 4; i++)
{
winArray[i].DrawWindow( );
}
Console.ReadLine();
}
}
Trong ví dụ này ta xây dựng một lớp MyWindow có một phương thức ảo:
public virtual void DrawWindow( )
Các lớp MyListBox, MyButton kế thừa từ lớp MyWindow và định nghĩa lại (override)
phương thức DrawWindow() theo cú pháp:
public override void DrawWindow( )
Sau đó trong hàm Main() ta khai báo và tạo một mảng các đối tượng
MyWindow. Vì mỗi phần tử thuộc mảng này là một tham chiếu thuộc lớp MyWindow
nên nó có thể trỏ tới bất kỳ một đối tượng nào thuộc các lớp kế thừa lớp MyWindow, chẳng hạn lớp MyListBox hay lớp MyButton.
Vòng lặp for đầu tiên tạo ngẫu nhiên các đối tượng thuộc một trong các lớp MyWindow, MyListBox, MyButton, vì vậy, tại thời điểm biên dịch chương trình, trình biên dịch không biết đối tượng thứ i thuộc lớp nào và do đó chưa thể xác định được đoạn mã của phương thức DrawWindow() cần gọi. Tuy nhiên, tại thời điểm chạy chương trình, sau vòng lặp for đầu tiên, mỗi winArray[i] tham chiếu tới một loại đối
tượng cụ thể nên trình thực thi sẽ tự động xác định được phương thức DrawWindow()
cần gọi. (Như vậy ta đã sử dụng một giao diện chung là DrawWindow() cho nhiều phương thức khác nhau).
61 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Chú ý rằng nếu phương thức DrawWindow() trong các lớp MyListBox,
MyButton,.. không có từ khóa override như cú pháp:
public override void DrawWindow( )
thì trình biên dịch sẽ báo lỗi.
3.2.3 Lớp trừu tượng, lớp cô lập, giao diện a. Lớp trừu tượng
Lớp trừu tượng (Abstract Class) là lớp dùng để định nghĩa những thuộc tính và
hành vi chung của những lớp khác… Hay nói cách khác lớp trừu tượng là lớp dùng để khai báo thuộc tính và phương thức cho các lớp khác sử dụng. Lớp trừu tượng được
dùng như một lớp cha (base class) của các lớp có cùng bản chất. Bản chất ở đây được
hiểu là kiểu, loại, nhiệm vụ của class. Mỗi lớp dẫn xuất (derived class - lớp con) có thể
thừa kế từ một lớp trừu tượng. Từ khóa abstract được dùng để định nghĩa một lớp trừu tượng. Những lớp được định nghĩa bằng cách dùng từ khóa abstract thì không cho
phép khởi tạo đối tượng (instance) của lớp ấy. Lớp trừu tượng (abstract class) có thể
coi như một quy tắc chung cho các lớp con (sub class) kế thừa từ nó. Khi các lớp kế
thừa từ nó thì bắt buộc phải định nghĩa lại (override) tất cả các phương thức đã được
khai báo abstract trong lớp trừu tượng.
Cú pháp khai báo phương thức trừu tượng: abstract public void TênPhươngThức( ); Phương thức trừu tượng phải được đặt trong lớp trừu tượng. Lớp trừu tượng có
từ khóa abstract đứng trước.
Cú pháp khai báo lớp trừu tượng:
abstract [thuộc tính truy cập] class {tên lớp}
Ví dụ:Xây dựng lớp HinhHocvới phương thức tính chu vi, diện tích là phương
thức trừu tượng hoặc phương thức ảo. Sau đó định nghĩa các lớp HinhChuNhat (hình
chữ nhật), HinhTron (hình tròn) kế thừa từ lớp HinhHoc với các thành phần dữ liệu và
phương thức tính chu vi, diện tích cụ thể của từng loại đối tượng.
// lop hinh hoc (truu tuong) abstract public class HinhHoc { abstract public doubleDienTich(); virtual public doubleChuVi() { return 0;} } // lop hinh tron ke thua tu lop hinh hoc
public classHinhTron : HinhHoc
{ double _bankinh;
62 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
public double BanKinh
{
get{ return_bankinh;}
set{ _bankinh = value;} }
public override doubleDienTich()
{
return _bankinh*_bankinh*3.1416; }
public override doubleChuVi()
{
return _bankinh*2*3.1416; }
}
// lop hinh chu nhat ke thua tu lop hinh hoc
public classHinhChuNhat : HinhHoc
{
double_dai, _rong;
public doubleChieuDai {
get{ return_dai;}
set{ _dai = value;}
}
public doubleChieuRong
{
get{ return_rong;}
set{ _rong = value;}
} public override doubleDienTich(){ return _dai*_rong;} public override doubleChuVi(){return( _dai+_rong )*2;} } classTester {
static voidMain(string[] args)
{ HinhHoc h;
63 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
HinhTron t = newHinhTron();
t.BanKinh = 5; Console.WriteLine("Thong tin ve hinh tron"); h = t;
Console.WriteLine("Chu vi hinh tron: {0} ",
h.ChuVi()); Console.WriteLine("Dien tich hinh tron:{0} ",
h.DienTich());
HinhChuNhat n = newHinhChuNhat();
n.ChieuDai = 4; n.ChieuRong = 3; h = n; Console.WriteLine("Thong tin ve hinh chu nhat
"); Console.WriteLine("Chu vi hinh chu nhat:{0}",
h.ChuVi());
Console.WriteLine("Dien tich hinh chu nhat:{0}", h.DienTich());
Console.ReadLine();
}
}
Trong lớp HinhHoc ở ví dụ trên, ta khai báo phương thức tính diện tích là một
phương thức trừu tượng (không có phần cài đặt mã của phương thức vì chưa biết đối
tượng hình thuộc dạng nào nên không thểtính diện tích của nó). Như vậy, lớp HinhHoc là một lớp trừu tượng.
abstract public double DienTich();
Trong lớp trên, ta cũng khai báo một phương thức tính chu vi là phương thức ảo
với mục đích minh họa rằng trong lớp trừu tượng có thể có phương thức ảo (hoặc bất
cứ một thành phần nào khác của một lớp). Vì đây là một phương thức không trừu
tượng nên phải có phần cài đặt mã bên trong thân phương thức. Các lớp HinhChuNhat,
HinhTron kế thừa từ lớp HinhHoc nên chúng buộc phải cài đặt lại phương thức tính
diện tích.
Trong hàm Main(), ta tạo ra đối tượng nthuộc lớp HinhChuNhat và đối tượng thuộc lớp HinhTron. Khi tham chiếu h thuộc lớp HinhHoc trỏ tới đối tượng (tương tự với đối tượng t), nó có thể gọi được phương thức tính diện tích của lớp HinhChuNhat (tương tự lớp HinhTron), điều này chứng tỏ phương thức trừu tượng cũng có thể dùng với mục đích đa hình.
Chú ý: Phân biệt giữa từ khóa new và override • Từ khóa override dùng để định nghĩa lại (ghi đè) phương thức ảo (virtual)
hoặc phương thức trừu tượng (abstract) của lớp cơ sở, nó được dùng với mục
đích đa hình.
64 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
• Từ khóa new để che dấu thành viên của lớp cơ sở trùng tên với thành viên
của lớp dẫn xuất.
b. Lớp cô lập
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ế thừa gây ra.
Nếu khai báo của lớp Window trong ví dụ 5.3 được thay đổi từ khóa
abstract bằng từ khóa sealed (cũng có thể loại bỏ từ khóa trong khai báo của
phương thức rawWindow()). 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:
‘ListBox’ cannot inherit from sealed class ‘Window’
Đâ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.
c. Giao diện
Các subclass trong C# không thể kế thừa nhiều hơn một class. Chính bởi điều
này C# không hỗ trợ đa kế thừa. Từ hạn chế này interface được giới thiệu. Một class trong C# thực thi được nhiều interface.
Giao diện là một dạng của lớp trừu tượng được sử dụng với mục đích hỗ trợ
tính đa hình. Trong giao diện không có bất cứ một cài đặt nào, chỉ có nguyên mẫu của
các phương thức, chỉmục, thuộc tính mà một lớp khác khi kếthừa nó thì phải có cài đặt
cụ thể cho các thành phần này (tức là lớp kế thừa giao diện tuyên bố rằng nó hỗ trợ các
phương thức, thuộc tính, chỉ mục được khai báo trong giao diện).
Khi một lớp kế thừa một giao diện ta nói rằng lớp đó thực thi (implement) giao
diện. Ta dùng từ khóa interface để khai báo một giao diện với cú pháp sau:
[MứcĐộTruyCập] interface TênGiaoDiện [:CácGiaoDiệnCơSở] {
//Nội dung
} MứcĐộTruyCập: public hoặc internal. CácGiaoDiệnCơSở: danh sách các interface khác mà nó kế thừa. Về mặt cú pháp, một giao diện giống như một lớp chỉ có phương thức trừu
trượng. Nó có thể chứa khai báo của phương thức, thuộc tính, chỉ mục, sự kiện
(events) nhưng không được chứa biến dữ liệu. Khi kế thừa một giao diện ta phải thực thi mọi phương thức, thuộc tính,… của giao diện.
65 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Chú ý:
• Mặc định, tất cả các thành phần khai báo trong giao diện đều là public. Nếu
có từ khóa public đứng trước sẽ bị báo lỗi. Các thành phần trong giao diện chỉ là các
khai báo, không có phần cài đặt mã.
• Một lớp có thể kế thừa một lớp khác đồng thời kế thừa nhiều giao diện.
Ví dụ: Mọi chiếc xe hơi hoặc xe máy đều có hành động (phương thức) khởi động và
dừng. Ta có thể dùng định nghĩa một giao diện kèm thêm một thuộc tính để cho biết
chiếc xe đã khởi động hay chưa: public interfaceIDrivable
{
void KhoiDong();
void Dung(); bool DaKhoiDong
{
get;
}
}
Thuộc tính đã khởi động (DaKhoiDong) chỉ có phương thức get vì khi gọi
phương thức KhoiDong() thì thuộc tính này sẽcó giá trị true, khi gọi phương thức Dung() thì thuộc tính này sẽ có giá trị false.
Khi đó, một lớp Car thực thi giao diện này phải cài đặt các phương thức và
thuộc tính đã khai báo trong giao diện Idrivable trên:
public interface IDrivable
{
void KhoiDong();
void Dung();
bool DaKhoiDong
{
get;
}
} public classCar:IDrivable { public bool Started = false;
public void KhoiDong()
{ Console.WriteLine("Xe ca khoi dong"); Started = true;
66 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
}
public voidDung()
{
Console.WriteLine("Xe ca dung"); Started = false;
}
public boolDaKhoiDong
{
get{return Started;}
}
}
public classTester
{
public static voidMain()
{
Car c = newCar();
c.KhoiDong();
Console.WriteLine("c.DaKhoiDong = " + c.DaKhoiDong);
c.Dung();
Console.WriteLine("c.DaKhoiDong = " + c.DaKhoiDong); Console.ReadLine();
}
}
3.3 Các lớp cơ sở trong .NET
3.3.1 System.Object
Trong chương này , chúng ta đã xem xét về system.object. chúng ta biết rằng đó
là lớp cơ sở chung mà mọi đối tượng khác được thừa kế và ta cũng xem xét về các
phương thức thành viên chính của nó. tuy nhiên, ta chưa tìm hiểu kỹ về khả năng của
tất cả các phương thức.trong phần này ta sẽ tìm hiểu các phương thức còn lại của system.object. Đầu tiên ta sẽ tìm hiểu tóm tắt của từng phương thức :
Phương thức Truy xuất Mục đích
string ToString() Trả về 1 chuỗi đại diện cho đối tượng public virtual
trả về mã băm của đối tượng được thiết kế public int GetHashCode() virtual cho phép ta tìm kiếm 1 cách hiệu quả các thể hiện của đối tượng trong từ điền
bool Equals(object obj) public so sánh đối tượng này với 1 đối tượng khác
67 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
virtual
bool Equals(object objA, public object objB) so sánh 2 đối tượng static
bool ReferenceEquals(object objA, object objB) public static so sánh các tham chiếu đối tượng để xem chúng có chỉ đến cùng đối tượng
trả về 1 đối tượng dẫn xuất từ System.Type Type GetType() public mà đưa ra chi tiết kiểu dữ liệu
Tạo ra một bản sao chép của đối tượng (nói
cách khác, sao chép dữ liệu trong đối tượng
nhưng không phải bất kỳ trường dữ liệu nào
object MemberwiseClone() protected
tham chiếu tới) Makes a shallow copy of the object (in other
words, copies data in the object but not
other objects any fields refer to)
protected void Finalize() Hàm hủy ( estructor) virtual
4 trong những phương thức này khai báo là ảo (virtual) vì vậy ta có thể
overload chúng.
Các thành viên của system.object:
ToString() - đây là cách trình bày chuỗi dễ dàng nhanh chóng và cơ bản. được
dùng trong tình huống khi bạn muốn phác hoạ nhanh nội dung của một đối
tượng,chẳng hạn để vá lỗi .nếu bạn muốn trình bày chuỗi phức tạp hơn bạn có thể dùng interface IFormatable.
GetHashcode() - hữu ích nếu đối tượng được đặt trong cấu trúc dữ liệu map (
hay còn gọi là bảng băm hoặc từ điển).Nó được dùng bởi những lớp mà thao tác những cấu trúc này để quyết định nơi đặt đối tượng trong cấu trúc .nếu bạn định lớp của bạn được sử dụng như là khoá trong từ điển, thì bạn sẽ cần overload GetHashcode().
Equals() ( cả 2 phiên bản) và ReferenceEquals()- có những khác biệt tinh vi
giữa 3 kiểu phương thức này, đi cùng với toán tử so sánh ,== ta sẽ xem xét sau.
Finalize() - đây là 1 destructor và được gọi khi một đối tượng tham chiếu là rác được thu nhặt để dọn dẹp tài nguyên. thực sự thì finalize() không làm gì cả , bị bỏ qua bởi bộ gom rác, nhưng điều này không đúng với các overload . Ta nên overload chỉ
khi cần thiết. Overload Finalize() trong C# không được làm tường minh (điều này hay
gây ra lỗi biên dịch) bằng cách cung cấp destructor; destructor được chuyển bởi trình biên dịch thành phươngg thức Finalize().
68 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
GetType() - phương thức này trả về thể hiện của một lớp được dẫn xuất từ
System.type. đối tượng này có thể cung cấp một số thông tin mở rộng về lớp mà đối
tượng là thành viên, bao gồm kiểu cơ sở, phương thức, thuộc tính ...
MemberWiseClone() - đây chỉ là thành viên của System.Object mà ta không đi sâu trong cuốn sách này. nó đơn giản tạo một bản sao của đối tượng và trả về 1 tham
khảo đến bản sao.lưu ý rằng bản sao được tạo như là bản sao bóng- nghĩa là nó sao
chép tất cả các kiểu giá trị trong lớp.nếu lớp chứa đựng những tham khảo kèm theo thì
chỉ có tham khảo được sao chép ,không phải là đối tượng.
So sánh các đối tượng tham chiếu tương đương Ta thấy rằng trong C# có đến 4 kiểu so sánh tương đương ( gồm 3 phương thức so
sánh và 1 toán tử = ). Ta sẽ tìm hiểu sự khác nhau giữa các loại so sánh này .
ReferenceEquals()
Tên của phương thức chính là ý nghĩa của nó . nó kiểm tra 2 tham chiếu liệu có
quy vào cùng một thể hiện của một lớp: rằng 2 tham chiếu này chứa đựng cùng một
địa chỉ trong bộ nhớ hay không.ReferenceEquals() luôn trả về true nếu được cung cấp
2 tham tham chiếu cùng quy vào một thể hiện đối tượng và sai nếu khác. tuy nhiên nó
xem null là bằng null.
SomeClass x, y;
x = new SomeClass(); y = new SomeClass();
bool B1 = ReferenceEquals(null, null); // trả về true
bool B2 = ReferenceEquals(null,x); // trả về false
bool B3 = ReferenceEquals(x, y); // trả về false bởi x và y trỏ đến 2 đối tượng
khác nhau.
Phương thức ảo Equals()
Là một phiên bản thể hiện của Equals() có thể được xem như là đối nghịch với
ReferenceEquals(). Đúng là phương thức Equals() làm việc bằng cách so sánh những
tham chiếu . phương thức này được cung cấp trong trường hợp ta muốn nạp chồng nó để so sánh giá trị của những thể hiện đối tượng.cụ thể ,nếu ta dự định thể hiện của lớp được sử dụng như khoá trong từ điển, ta cần nạp chồng phương thức để so sánh giá trị. nếu không thì tuỳ thuộc vào cách ta nạp chồng GetHashCode(), lớp từ điển chứa đựng những đối tượng của ta cũng không làm việc hoàn toàn, hoặc là làm việc thiếu khả năng.1 điểm mà ta chú ý khi nạp chồng Equals() là việc nạp chồng của ta sẽ không bao giờ tung ra biệt lệ (biệt lệ là lỗi sinh ra trong khi thực thi).
Phương thức static Equals()
Phương thức static Equals() thực sự thì cũng giống như phiên bản phương thức ảo Equals().điểm khác là phương thức static có thể đối phó khi đối tượng là null, và do
69 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
đó cung cấp 1 kiểu bảo vệ an toàn chống lại việc tung ra biệt lệ nếu đối tượng có thể là
null.phương thức này đầu tiên sẽ kiểm tra khi overload thì tham chiếu được truyền có
phải là null không.nếu cả hai là null, thì nó trả về true, ( vì null được xem là bằng
null). nếu chỉ có một cái là null. nó trả về false.nếu cả hai tham chiếu thực sư quy đến một thứ,thì nó gọi phiên bản thể hiện ảo của Equals() nghĩa là khi bạn nạp chồng bản
thể hiện của equals() ,kết quả cũng như bạn nạp chồng lên bản static.
Toán tử so sánh (==)
Toán tử so sánh có thể được xem như là bản trung gian giữa việc so sánh giá trị
và việc so sánh tham chiếu. thường viết :
bool b = (x == y); // x, y là đối tượng tham chiếu
nghĩa là ta đang so sánh 2 tham chiếu. tuy nhiên,chấp nhận rằng có 1 vài lớp mà ý
nghĩa của nó trực quan hơn nếu xem nó như là một giá trị.trong trường hợp đó, tốt hơn hết là nạp chồng toán tử so sánh thành so sánh giá trị. ví dụ như là những chuỗi mà
microsoft nạp chồng toán tử này, bởi vì khi các nhà phát triển nghĩ đến việc so sánh
chuỗi, họ luôn nghĩ rằng phải so sánh nội dung của chuỗi hơn là so sánh tham chiếu.
Nếu ta muốn nạp chồng những hàm so sánh và Object.GetHashCode() cần lưu ý
những hướng dẫn sau: không nên nạp chồng chỉ 1 Object.Equals() hay
Object.GetHashCode() . nếu ta nạp chồng cái naỳ thì cũng phải nạp chồng cái kia.bởi
vì việc thực thi từ điển đòi hỏi cả 2 phương thức hoạt động đồng nhất (nếu Obkect.Equals() làm việc so sánh giá trị, thì GetHashCode() cũng nên xây dựng mã
dựa trên giá trị.) Nếu ta nạp chồng toán tử == , thì phải nạp chồng Object.Equals() ( và
vì vậy cũng nạp chồng object.GetHashCode() ) bởi vì nếu == so sánh giá trị , thì
object.Equals() cũng so sánh giá trị.
So sánh các kiểu giá trị trong equal
Khi so sánh kiểu giá trị trong equal , nguyên tắc cũng giống như kiểu tham
chiếu: ReferenceEquals() được dùng để so sánh tham chiếu, Equals() được dùng cho
so sánh giá trị, và toán tử so sánh được xem như trường hợp trung gian.tuy nhiên sự
khác biệt ở đây là kiểu giá trị cần được bỏ vào hộp (boxed) để chuyển thành kiểu tham chiếu.do đó microdoft nạp chồng phương thức Equals() vào lớp System.ValueType để cung cấp ý nghĩa tương đương đến kiểu giá trị.nếu ta gọi sA.Equals(sB), sA, sB là thể hiện của một vài Struct, thì giá trị trả về sẽ là true hoặc false tuỳ theo liệu sA và sB chứa đựng cùng giá trị trong tất cà các trường. mặt khác, không có việc nạp chồng == bằng mặc định trong stuct riêng của ta.Viết ( sA==sB) trong bất kì biểu thức nào thì đều bị lỗi trừ khi ta cung cấp 1 overload == vào mã trong Struct. 1 điểm khác là
ReferenceEquals() luôn trả về false khi ứng dụng vào kiểu giá trị, bởi vì để gọi phương
thức, kiểu giá trị sẽ cần bỏ vào hộp thành đối tượng . thậm chí nếu ta viết: bool b = ReferenceEquals(v,v); // v là biến của một vài kiểu giá trị
70 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Ta sẽ có câu trả lời là false bởi vì v sẽ được đóng hộp riêng rẽ khi chuyển mỗi thông
số,nghĩa là ta sẽ có sự tham chiếu khác nhau. Gọi referenceEquals() để so sánh kiểu
giá trị không phải là 1 sự chọn lựa hay. Mặc dù nạp chồng mặc định của Equals() cung
cấp bởi System.ValueType hầu như chắc chắn tương thích với số lượng lớn cấu trúc mà ta định nghĩa, ta có thể muốn nạp chồng nó lần nữa theo ý riêng của ta để cải thiện
việc thực thi.nếu 1 kiểu giá trị chứa kiểu tham chiếu như 1 trường, ta có thể muốn nạp
chồng Equals() để cung cấp ngữ nghĩa tương đương cho những trường này, mặc định
thì Equals() đơn giản sẽ so sánh điạ chỉ của chúng.
3.3.2 Xử lý mảng
Ngôn ngữ C# cung cấp cú pháp chuẩn cho việc khai báo những đối tượng
Array. Tuy nhiên, cái thật sự được tạo ra là đối tượng của kiểu System.Array. Mảng
trong ngôn ngữ C# kết hợp cú pháp khai báo mảng theo kiểu ngôn ngữ C và kết hợp với định nghĩa lớp do đó thể hiện của mảng có thể truy cập những phương thức và
thuộc tính của System.Array.
Một số các thuộc tính và phương thức của lớp System.Array
Truy xuất Mô tả Phương thức
Tìm kiếm một mảng một chiều đã sắp thứ tự. BinarySearch() public
static
thiết lập các thành phần của mảng về 0 hay null. Clear() public
static
thực hiện sao chép một vùng của mảng vào Copy() public
mảng khác. static
CreateInstance() tạo một thể hiện mới cho mảng public
static
trả về chỉ mục của thể hiện đầu tiên IndexOf() public
chứa giá trị trong mảng một chiều static
LastIndexOf()
trả về chỉ mục của thể hiện cuối cùng của giá trị trong mảng một chiều public static
Reverse()
đảo thứ tự của các thành phần trong mảng một chiều public static
sắp xếp giá trị trong mảng một chiều. Sort()
public static
tính public
trả về giá trị bool thể hiện mảng có kích thước cố định hay không. Thuộc IsFixedSize
tính public
trả về giá trị bool thể hiện mảng chỉ đọc hay không Thuộc IsReadOnly
71 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
IsSynchronized giá trị bool thể hiện mảng có hỗ trợ thread-safe public
Length public chiều dài của mảng
Rank public chứa số chiều của mảng
SyncRoot public
chứa đối tượng dùng để đồng bộ truy cập trong mảng
GetEnumerator() public trả về Ienumerator
GetLength() public trả về kích thước của một chiều cố định trong
mảng
GetLowerBound() public trả về cận dưới của chiều xác định trong mảng
GetUpperBound() public trả về cận trên của chiều xác định trong mảng
Initialize() public
Khởi tạo tất cả giá trị trong mảng kiểu giá trị bằng cách gọi bộ khởi dụng mặc định của từng
giá trị.
SetValue() public thiết lập giá trị cho một thành phần xác định
trong mảng.
3.3.3 Xử lý chuỗi
Qua chương 2, ta đã xem xét về chuỗi và thấy rằng từ khoá String trong C# thực sự
tham khảo lớp cơ sở system.String. System.string là lớp rất linh hoạt và mạnh , không
phải chỉ là lớp có liên quan đến chuỗi trong .NET. trong phần này ta sẽ xem lại những
đặc tính của System.String, sau đó sử dụng chuỗi để ứng dụng trong môt số lớp .NET -
cụ thể là lớp System.Text và namespace System.Text.RegularExpressions .
Xây dựng chuỗi - nếu ta hay lặp lại việc thay đổi trên 1 chuỗi , ví dụ để định 1
độ dài cho chuỗi trước khi trình bày nó hoặc truyền nó đến vài phương thức
hoặc phần mềm,lớp chuỗi có thể không đủ khả năng để làm.trong tình huống
này, 1 lớp khác , System.Text.StringBuilder thích hợp hơn, bởi vì nó được thiết
kế để làm trong các tình huống này.
Các biểu thức định dạng - ta sẽ xem xét kĩ hơn những biểu thức định dạng sử dụng Console.Writeline(). những biểu thức định dạng này sử dụng vài interface. IFormatProvider và IFormattable,bằng việc sử dụng các interface này trong lớp riêng , ta có thể định nghĩa những chuỗi định dạng riêng để console.Writeline() và những lớp quen thuộc sẽ trình bày giá trị trong lớp của ta theo bất cứ cách nào mà ta chỉ định.
Biểu thức chính quy ( regular expressions )- .NET cũng đưa ra một số lớp phức tạp mà đưọc dùng khi ta cần xác định hoặc trích ra chuỗi con thoả mãn 1
điều kiện phức tạp từ 1 chuỗi dài.ví dụ như cần tìm tất cả các lần xuất hiện của 1 kí tự hay 1 tập kí tự được lặp lại.hoặc cần tìm tất cả các từ bắt đầu với 's' và
72 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
chứa ít nhất 1 kí tự 'n'.mặc dù ta có thể viết phương thức để làm điều này chỉ
bằng việc dùng lớp chuỗi ,nhựng nó rất cồng kềnh. thay vào đó , ta có thể dùng
1 vài lớp trong System.Text.RegularExpressions mà đưọc thiết kế để thực thi
các quy trình này.
System.String
Trước khi kiểm tra các lớp chuỗi khác, ta sẽ xem lại nhanh những phương thức
trong lớp chuỗi. System.String là lớp được thiết kế để lưu trữ chuỗi, bao gồm 1 số lớn
các thao tác trên chuỗi.không chỉ thế mà còn bởi vì tầm quan trọng của kiểu dữ liệu này , C# có từ khoá riêng cho nó và kết hợp với cú pháp để tạo nên cách dễ dàng trong
thao tác chuỗi.
Ta có thể nối chuỗi :
string message1 = "Hello"; message1 += ", There";
string message2 = message1 + "!";
Trích 1 phần chuỗi dùng chỉ mục:
char char4 = message[4]; // trả về 'a', lưu ý rằng kí tự bắt đầu tính từ chỉ mục 0
Các phương thức khác (sơ lược):
Phương thức Mục đích
Compare so sánh nội dung của 2 chuỗi
giống compare nhưng không kể đến ngôn ngữ bản địa hoặc văn hoá
CompareOrdinal (as compare but doesn't take culture into account)
định dạng một chuỗi chứa 1 giá trị khác và chỉ định cách mỗi giá trị Format nên được định dạng.
IndexOf vị trí xuất hiện đầu tiên của 1 chuỗi con hoặc kí tự trong chuỗi
IndexOfAny vị trí xuất hiện đầu tiên của bất kì 1 hoặc 1 tập kí tự trong chuỗi
LastIndexOf giống indexof , nhưng tìm lần xuất hiện cuối cùng
LastIndexOfAny giống indexofAny , nhưng tìm lần xuất hiện cuối cùng
PadLeft canh phải chuỗi điền chuỗi bằng cách thêm 1 kí tự được chỉ định lặp lại vào đầu chuỗi
PadRigth canh trái chuỗi điền chuỗi bằng cách thêm 1 kí tự được chỉ định lặp lại vào cuối chuỗi
thay thế kí tự hay chuỗi con trong chuỗi với 1 kí tự hoặc chuỗi con Replace khác
73 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
chia chuỗi thành 2 mảng chuỗi con ,ngắt bởi sự xuất hiện của một kí Split tự nào đó
Substring trả về chuỗi con bắt đầu ở một vị trí chỉ định trong chuỗi.
ToLower chuyển chuỗi thành chữ thuờng
ToUpper chuyển chuỗi thành chữ in
Trim bỏ khoảng trắng ở đầu và cuối chuỗi
Xây dựng chuỗi
Chuỗi là 1 lớp mạnh với nhiều phương thức hữu ích , tuy nhiên chuỗi gặp khó
khăn trong việc lặp lại sự thay đổi đến chuỗi ban đầu.nó thực sự là kiểu dữ liệu không
biến đổi, nghĩa là mỗi lần ta khởi động 1 đối tượng chuỗi, thì đối tượng chuỗi đó
không bao giờ được thay đổi.những phương thức hoặc toán tử mà cập nhật nội dung
của chuỗi thực sự là tạo ra một chuỗi mới , sao chép chuỗi cũ vào nếu cần thiết. Ví dụ:
string greetingText = "Hello from all the guys at Wrox Press. ";
greetingText += "We do hope you enjoy this book as much as we enjoyed
writing it.";
Đầu tiên lớp system.String được tạo và khởi tạo giá trị “Hello from all the
people at Wrox Press.” chú ý khoảng trắng sau sau dấu chấm. Khi điều này xảy ra,
thời gian chạy .NET sẽ định vị đủ bộ nhớ trong chuỗi để chứa đoạn kí tự này (39 kí tự
) và tạo ra 1 biến greetingText để chuyển đến 1 thể hiện chuỗi. Ở dòng tiếp theo, khi ta
thêm kí tự vào. Ta sẽ tạo ra một chuỗi mới với kích thước đủ để lưu trữ cả hai đoạn
(103 kí tự ).đoạn gốc "Hello from all the people at Wrox Press. ", sẽ được sao chép vào
chuỗi mới với đoạn thêm "We do hope you enjoy this book as much as we enjoyed
writing it. " sau đó địa chỉ trong biến greetingText được cập nhật, vì vậy biến sẽ trỏ đúng đến đối tượng chuỗi mới.chuỗi cũ không còn được tham chiếu - không có biến
nào truy vào nó- và vì vậy nó sẽ được bộ thu gom rác gỡ bỏ. Giả sử ta muốn mã hoá
chuỗi bằng cách thay đổi từng kí tự với 1 kí tự ASCII. điều này sẽ trả vế chuỗi :
"Ifmmp gspn bmm uif hvst bu Xspy Qsftt. Xf ep ipqf zpv fokpz uijt cppl bt nvdi bt xf fokpzfe xsjujoh ju." có nhiều cách làm điều này nhưng cách đơn giản nhất là dùng phương thức String.replace() mà thay thế chuỗi con này bằng chuỗi con khác, dùng Replace() ta viết đoạn mã mã hoá như sau :
string greetingText = "Hello from all the guys at Wrox Press. "; greetingText += "We do hope you enjoy this book as much as we enjoyed writing it.";
for(int i = (int)'z'; i>=(int)'a' ; i--)
{ char old = (char)i;
74 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
char new = (char)(i+1);
greetingText = greetingText.Replace(old, new);
}
for(int i = (int)'Z'; i>=(int)'A' ; i--)
{
char old = (char)i;
char new = (char)(i+1); greetingText = greetingText.Replace(old, new);
}
Console.WriteLine("Encoded:\n" + greetingText);
Vậy cần bao nhiêu vùng nhớ làm điều này? Replace() làm việc theo cách thông minh, để mở rộng nó sẽ không tạo ra một chuỗi mới trừ khi nó thực sự phải thay đổi
chuỗi cũ.chuỗi gốc có 23 kí tự chữ thường khác nhau và 3 kí tự in khác nhau,Replace()
sẽ định vị 1 chuỗi mới tổng cộng 26 lần,mỗi chuỗi mới lưu trữ 103 kí tự.nghĩa là kết
quả quy trình mã hoá sẽ là đối tượng chuỗi có khả năng lưu trữ kết hợp tổng cộng
2678 kí tự bây giờ đang nằm trong heap để được thu dọn.Rõ ràng nếu ta sử dụng chuỗi
để làm điều này, ứng dụng của ta sẽ chạy không tốt. Để giải quyết Microsoft cung cấp
lớp System.Text.StringBuilder . lớp System.Text.StringBuilder không mạnh như lớp chuỗi tính theo thuật ngữ là số phương thức nó có .tiến trình mà ta có thể làm trên
Stringbuilder được giới hạn thành thay thế hoặc mở rộng hoặc bỏ đoạn từ chuỗi. Tuy
nhiên nó làm việc theo cách hiệu quả hơn. Bất cứ nơi nào ta xây dựng chuỗi, chỉ đủ
vùng nhớ được định vị để giữ chuỗi,Stringbuidler sẽ định vị vùng nhớ nhiều hơn cần.ta
có thể chỉ định vùng nhớ được định vị , nếu không , thì số mặc định tuy thuộc vào kích
cỡ của chuỗi mà StringBuilder được khởi động cùng .nó có 2 thuộc tính chính:
- Length - độ dài của chuỗi thực sự chứa
- Capacity - độ daì chuỗi mà nó chỉ định đủ vùng nhớ để lưu trữ.
Bất kì cập nhật nào đến chuỗi sẽ được làm trong khối vùng nhớ này,như là viết thêm chuỗi con hoặc thay thế các kí tự riêng trong chuỗi rất tốt.Việc gỡ bỏ hoặc chèn chuỗi con vẫn không tốt, bởi vì những phần theo sau chuỗi phải bị di chuyển.chỉ nếu ta thực thi vài thao tác mà tăng dung lượng chuỗi sẽ tạo ra vùng nhớ mới cần được định vị và toàn bộ chuỗi được chứa có thể được di chuyển.vào lúc viết ,Microsoft không cho biết làm cách nào để dung lượng thêm được thêm vào, nhưng từ kinh nghiệm cho thấy StringBuilder xuất hiện với dung lượng gấp đôi của nó nếu nó thăm dò thấy dung
lương bị vượt quá và không có giá trị mới của dung lương được thiết đặt rõ ràng.
Ví dụ , nếu ta sử dụng đối tượng StringBuilder để tạo ra chuỗi chào đón gốc, ta viết:
75 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
StringBuilder greetingBuilder = new StringBuilder("Hello from all the guys at
Wrox Press. ", 150);
greetingBuilder.Append("We do hope you enjoy this book as much as we
enjoyed writing it"); Trong mã này . ta thiết lập dung lương khởi tạo là 150 cho StringBuilder. nó
luôn là ý tưởng hay để thiết lập dung lượng mà bao phủ độ dài lớn nhất của chuỗi , để
bảo đảm String builder không cần tái định vị bởi vì dung lượng của nó lớn .vì vậy ta
có thể thiết lập 1 số int cho dung lượng, mặc dù nếu ta cố định vị vượt quá 2 tỷ ký tự hệ thống sẽ cảnh báo. Khi mã trên thực thi , đầu tiên ta tạo ra 1 đối tượng
StringBuilder khởi tạo như sau: Khi gọi phương thức append() ,đoạn còn lại đuợc đặt
trong khoảng trống, không cần định vị thêm vùng nhớ.tuy nhiên khả năng thực sự của
StringBuilder chỉ thấy rõ khi lặp lại việc thay thế đoạn. ví dụ, nếu ta cố mã hoá đoạn văn bản theo cách trên ,ta có thể mã hoá toàn bộ mà không cần định vị thêm bất kì
vùng nhớ nào nữa :
StringBuilder greetingBuilder =
new StringBuilder("Hello from all the guys at Wrox Press. ", 150);
greetingBuilder.Append("We do hope you enjoy this book as much as we
enjoyed writing it");
for(int i = (int)'z'; i>=(int)'a' ; i--)
{
char old = (char)i;
char new = (char)(i+1);
greetingBuilder = greetingBuilder.Replace(old, new);
}
for(int i = (int)'Z'; i>=(int)'A' ; i--)
{ char old = (char)i; char new = (char)(i+1); greetingBuilder = greetingBuilder.Replace(old, new); } Console.WriteLine("Encoded:\n" + greetingBuilder.ToString());
Đoạn mã này dùng phương thức StringBuilder.Replace() mà giống như
String.Replace() nhưng không có sao chép chuỗi trong lúc làm.tổng vùng nhớ được định vị để giữ chuỗi trong đoạn mã trên là 150 cho người xây dựng, cũng như vùng
76 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
nhớ chỉ định trong suốt việc thao tác chuỗi thi hành bên trong trong câu lệnh cuối
Console.Writeline()
thông thường , ta sử dụng StringBuilder để thi hành bất kì thao tác của chuỗi, và String
để lưu trữ hoặc trình bày kết quả cuối cùng.
Các thành viên của StringBuilder
Chúng ta đã minh hoạ 1 hàm dựng của StringBuilder, mà lấy thông số khởi tạo
là chuỗi và dung lượng.cũng có một vài cái khác , trong số chúng, ta có thể cung cấp
chỉ 1 chuỗi :
StringBuilder sb = new StringBuilder("Hello");
hoặc chỉ cung cấp dung luợng , chuỗi trống:
StringBuilder sb = new StringBuilder(20);
Ngoại trừ 2 thuộc tính trên ta còn có thuộc tính chỉ đọc MaxCapacity chỉ định giới hạn mà 1 thể hiện của StringBuilder cho phép .mặc định , số này là int.MaxValue
( khoảng 2 tỷ).
// dung lượng khởi tạo là 100 nhưng lớn nhất là 500
// do đó StringBuilder không thể phát triển hơn 500 kí tự được.
// chúng sẽ tung biệt lệ nếu ta cố làm điều đó.
StringBuilder sb = new StringBuilder("Hello", 100, 500);
StringBuilder sb = new StringBuilder(100, 500); Ta có thể thiết lập dung lượng ở bất cứ đâu .nếu dung lượng thiêt lập nhỏ hơn
chuỗi hiện hành hoặc lớn hơn độ dài lớn nhất thì biệt lệ sẽ được tung ra.
StringBuilder sb = new StringBuilder("Hello");
sb.Capacity = 100;
Phương thức chính StringBuilder bao gồm :
- Append() thêm 1 chuỗi vào chuỗi đương thời AppendFormat(): thêm 1 chuỗi
mà được trình bày từ các chỉ định định dạng
- Insert() :chèn 1 chuỗi con vào chuỗi đương thời
- Remove() :bỏ các kí tự từ chuỗi đương thời - Replace() :thay thế kí tự này bằng kí tự khác hoặc chuỗi con này bằng chuỗi
con khác trong chuỗi đương thời
- ToString() :trả về chuỗi đương thời ép thành 1 đối tượng System.Object ( nạp
chồn từ System.Object)
Vào lúc viết, không thể ép kiểu (tường minh hay không tưòng minh) từ StringBuilder sang String. nếu ta muốn xuất nội dung của StringBuilder như là 1
String , cách duy nhất để làm là dùng phương thức Tostring()
Định dạng Chuỗi
77 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Nếu ta muốn những lớp mà ta viết thân thiện với người sử dụng , thì chúng cần
để trình bày chuỗi theo bất cứ cách nào mà người sử dụng muốn dùng.Thời gian chạy
.NET định nghĩa 1 cách chuẩn để làm : dùng 1 interface hoặc IFormatable.biểu diễn
làm thế nào để thêm những đặc tính quan trọng đến lớp của ta và những cấu trúc là chủ đề của phần này. Ta thường chỉ định định dạng của biến được trình bày khi gọi
Console.Writeline. do đó ta sẽ lấy phương thức này làm ví dụ, mặc dù hầu hết những
điều ta sắp học có thể ứng dụng trong bất cứ tình huống nào mà ta muốn định dạng
chuỗi Ví dụ:
double d = 13.45;
int i = 45;
Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i); Chuỗi định dạng tự nó bao gồm hầu hết văn bản được trình bày,nhưng bất cứ ở
đâu có biến được định dạng , chỉ mục của nó trong danh sách thông số trong dấu
ngoặc.có thể là thông tin khác bên trong dấu ngoặc về việc định dạng của mục đó:
số kí tự được giữ bởi sự trình bày của mục có thể xuất hiện, thông tin này sẽ có dấu
phảy đứng trước. Một số âm chỉ định rằng mục đó đưọc canh trái, trong khi 1 số
dương chỉ định mục đó được canh phải. nếu mục đó giữ nhiều kí tự hơn được yêu cầu,
nó vẫn xuất hiện đầy đủ. Một chỉ định định dạng cũng có thể xuất hiện.điều này sẽ được đặt trước bởi dấu hai chấm và chỉ định cách ta muốn mục được định dạng. ví dụ
ta muốn định dạng số như kiểu tiền tệ hoặc trình bày theo ký hiệu khoa học ?
Đặc tả Áp dụng đến Ý nghĩa Ví dụ
numeric types locale-specific $4834.50 (USA)£4834.50 C
monetary value (UK)
integer types only general integer 4834 D
numeric types scientific notation 4.834E+003 E
numeric types fixed point decimal 4384.50 F
numeric types general number 4384.5 G
numeric types N
usual locale specific format for numbers 4,384.50 (UK/USA)4 384,50 (continental Europe)
numeric types Percentage notation 432,000.00% P
integer types only hexadecimal format 1120 (NB. If you want to X
display 0x1120, you'd need to
write out the 0x separately)
78 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Làm thế nào chuỗi được định dạng Xem ví dụ sau:
Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i); Thực vậy, Console.Writeline() chỉ việc kiểm soát toàn bộ tập thông số bằng
cách chuyển đến phương thức static, String.Format()- cũng phương thức này được gọi
nếu ta muốn định dạng các giá trị trong chuỗi theo các mục đích khác , như là trình
bày trong textbox.Để làm rõ những gì mã nguồn thực sự thực thi phương thức này là gì ,ta sẽ xem xét việc thi hành phương thức oveload với 3 thông số của phương thức
Writeline() như sau :
// giống như thực thi Console.Writeline()
public void WriteLine(string format, object arg0, object arg1)
{
Console.WriteLine(string.Format(format, arg0, arg1));
}
Overload 1 thông số của phương thức này, đơn giản viết nội dung của chuỗi
được trình bày, không làm bất cứ định dạng gì trên nó. String.format() cần xây dựng
chuỗi cuối bằng cách thay thế mỗi phần đặc tả định dạng bằng việc trình bày chuỗi thích hợp của đối tượng tương ứng.tuy nhiên như đã biết , chính xác trong tình huống
này ta cần thể hiện Stringbuilder hơn là thể hiện string. trong ví dụ ta đang xét, 1 thể
hiện StringBuilder sẽ được tạo ra và khởi tạo với phần đầu là chuỗi “The double is”.
Phương thức StringBuilder.AppendFormat() sẽ được gọi, truyền phần đặc tả định dạng
dầu tiên , {0,10:E} ,và đối tượng kết hợp kiểu double này sẽ được thêm vào chuỗi
đang được xây dựng, quy trình này sẽ tiếp tục với việc gọi nhiều lần
StringBuilder.Append() và StringBuilder.AppendFormat() cho đến khi toàn bộ chuỗi
được định dạng đã xong. Bởi vì StringBuilder.AppendFormat() sẽ cần minh họa cách
định dạng thực sự đối tượng. điều đầu tiên sẽ là thăm dò đối tượng để xem liệu nó có thực thi 1 interface IFormatable ( trong namespace System ) hay chưa.ta có thể thử ép kiểu 1 đối tượng thành 1 interface và xem coi ép kiểu được không. nếu kiểm tra thất bại, thì AppendFormat() đơn giản gọi phương thức toString() của đối tượng, (do phương thức này được tất cả các đối tượng cùng thừa kế từ System.Object hoặc do nạp chồng). IFormattable định nghĩa giống như một phương thức, mà cũng được goị ToString().tuy nhiên phương thức này lấy 2 thông số, đối lập với phiên bản
System.Object , mà không lấy bất kì thông số nào. đây là định nghĩa cho IFormattable:
interface IFormattable {
79 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
string ToString(string format, IFormatProvider formatProvider);
}
Thông số đầu tiên mà hàm overload của Tostring() này lấy là chuỗi mà đặc tả định dạng được yêu cầu.nói cách khác nó chỉ định phần chuỗi xuất hiện trong { } so
với chuỗi gốc được truyền đến Console.WriteLine() hay String.Format(). Ví dụ:
Console.WriteLine("The double is {0,10:E} and the int contains {1}", d, i);
Khi tính thông số đầu tiên, {0,10:E}, hàm sẽ gọi biến double, d, và thông số đầu tiên đưọc truyền đến nó sẽ là E. những gì StringBuilder.AppendFormat() sẽ truyền ở
đây là bất cứ đoạn văn bản nào xuất hiện sau dấu hai chấm trong phần đặc tả định dạng
từ chuỗi gốc. Ta không quan tâm về thông số thứ 2 của Tostring(). Nó là 1 tham chiếu
đến đối tượng mà thực thi interface IFormatProvider.Interface này gửi thông tin mà Tostring() có thể cần khi định dạng đối tượng. Quay trở lại ví dụ trên, mục đầu tiên ta
muốn định dạng là double với phần đặc tả định dạng là E.như đã đề cập, phương thức
StringBuilder.AppendFormat() sẽ thiết lập double thi hành IFormattable, và do đó sẽ
gọi hàm overload Tostring() 2 thông số, truyền vào nó chuỗi E cho thông số đầu tiên
và null cho thông số thứ hai.bây giờ nó tuỳ thuộc vào sự thi hành của phương thức này
mà sẽ trả về 1 chuỗi trình bày kiểu double theo định dạng. Nếu cần
StringBuilder.AppendFormat() sẽ lựa chọn điền vào chuỗi trả về với khoảng trắng, để đủ 10 kí tự trong phần đặc tả định dạng của chuỗi trong trường hợp này.
Đối tượng kế tiếp được định dạng là kiểu int, mà không yêu cầu định dạng cụ thể.vì
vậy StringBuilder.AppendFormat() sẽ truyền vào tham chiếu null cho định dạng chuỗi
này. Toàn bộ quy trình có thể được tóm tắt như sau:
Ví dụ FormattableVector:
Trong ví dụ này phần đặc tả định dạng mà ta sẽ hổ trợ là: N - được phiên dịch như là yêu cầu cung cấp 1 số được biết như là Norm của Vector.là tổng bình phương của các thành phần của nó và luôn được trình bày giữa dấu || , ví dụ như || 34.5 ||
VE - được phiên dịch như là yêu cầu trình bày mỗi thành phần trong đặc tả định
dạng.như đặc tả E đối với 1 số double chỉ định (2.3E+01, 4.5E+02, 1.0E+00).
IJK - được phiên dịch như là yêu cầu trình bày vector dưới dạng 23i + 450i +
1k, việc trình bày mặc định sẽ có dạng Vector( 23,450,1.0)
80 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Để cho đơn giản ta sẽ không thi hành bất kì tuỳ chọn để trình bày Vector theo
kết hợp giữa IJK và định dạng khoa học.tuy nhiên ta sẽ có thể kiểm tra đặc tả theo
cách không phân biệt chữ hoa và chữ thường , để cho phép ijk thay IJK. lưu ý rằng
hoàn toàn tuỳ thuộc vào chuỗi ta sử dụng để chỉ định đặc tả định dạng. Đầu tiên ta sẽ khai báo Vector thi hành Iformatable, sao đó thêm vào phương thức
overload tostring() 2 thông số như mã code sau:
struct Vector : IFormattable
{ public double x, y, z;
public string ToString(string format, IFormatProvider formatProvider)
{
if (format == null) return ToString();
string formatUpper = format.ToUpper();
switch (formatUpper)
{
case "N":
return "|| " + Norm().ToString() + " ||";
case "VE": return String.Format("( {0:E}, {1:E}, {2:E} )", x, y, z);
case "IJK":
StringBuilder sb = new StringBuilder(x.ToString(), 30);
sb.Append(" i + ");
sb.Append(y.ToString());
sb.Append(" j + ");
sb.Append(z.ToString());
sb.Append(" k");
return sb.ToString(); default: return ToString(); } } Đó là tất cả những gì ta phải làm. lưu ý cần kiểm tra định dạng null trước khi gọi bất cứ phương thức nào.trong ví dụ phần đặc tả VE , ta cần mỗi thành phần đưọc
định dạng theo cú pháp khoa học,vì thế ta dùng String.Format()để làm.tất cả các
trường x,y,z là double.Trong trường hợp định dạng IJK, có vài chuỗi con được thêm
81 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
vào chuỗi, vì thế ta dùng đối tượng StringBuilder để làm. Để hoàn chỉnh ta sẽ xây
dựng lại phương thức overload Tostring() không thông số:
public override string ToString()
{ return "( " + x + " , " + y + " , " + z + " )";
}
cuối cùng ta thêm vào phương thức Norm() mà tính bình phương ( Norm) của Vector:
public double Norm() {
return x*x + y*y + z*z;
}
Bây giờ ta sẽ thử đoạn mã trên theo vài cách : static void Main()
{
Vector v1 = new Vector(1,32,5);
Vector v2 = new Vector(845.4, 54.3, -7.8);
Console.WriteLine("\nIn IJK format,\nv1 is {0,30:IJK}\nv2 is {1,30:IJK}",
v1, v2);
Console.WriteLine("\nIn default format,\nv1 is {0,30}\nv2 is {1,30}", v1, v2);
Console.WriteLine("\nIn VE format\nv1 is {0,30:VE}\nv2 is {1,30:VE}", v1,
v2);
Console.WriteLine("\nNorms are:\nv1 is {0,20:N}\nv2 is {1,20:N}", v1,
v2);
}
Kết quả trả về là :
FormattableVector In IJK format, v1 is 1 i + 32 j + 5 k v2 is 845.4 i + 54.3 j + -7.8 k In default format, v1 is ( 1 , 32 , 5 ) v2 is ( 845.4 , 54.3 , -7.8 )
In VE format v1 is ( 1.000000E+000, 3.200000E+001, 5.000000E+000 )
82 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
v2 is ( 8.454000E+002, 5.430000E+001, -7.800000E+000 )
Norm là :
v1 is || 1050 || v2 is || 717710.49 ||
3.3.4 Biểu thức chính quy ( Regular Expression)
Biểu thức chính quy là 1 dạng kĩ thuật nhỏ mà hữu ích trong 1 vùng rộng lớp
của chương trình, mặc dù không được nhiều nhà phát triển biết đến. nó có thể hiểu như là 1 ngôn ngữ nhỏ dùng cho mục đích: để tìm chuỗi con trong biểu thức chuỗi lớn.nó
không phải là một kĩ thuật mới, xuất phát từ môi trường UNIX, đuợc dùng với PERL.
Microsoft cho nó vào Windows,và cho đến giờ thì nó hầu như được sử dụng với những
ngôn ngữ kịch bản.Biểu thức chính quy được hổ trợ bởi một số lớp .NET trong namespace: System.Text.RegularExpressions.
Giới thiệu về Biểu thức chính quy.
Ngôn ngữ biểu thức chính quy là ngôn ngữ được thiết kế đặc biệt cho việc xử lí
chuỗi chứa đựng 2 đặc tính:
- 1 tập mã escape cho việc xác định kiểu của các kí tự . ta quen với việc dùng kí
tự * để trình bày chuỗi con bất kì trong biểu thức OS . biểu thức chính quy dùng
nhiều chuỗi như thế để trình bày các mục như là 'bất kì 1 kí tự' ,'1 từ ngắt ','1 kí tự tuỳ chọn',...
- 1 hệ thống cho việc nhóm những phần chuỗi con, và trả về kết quả trong suốt
thao tác tìm. Dùng biểu thức chính quy , có thể biểu diễn những thao tác ở cấp cao và
phức tạp trên chuỗi.ví dụ :
- Xác định tất cả các từ lặp lại trong chuỗi , chuyển “The computer books books”
thành “The computer books”.
- Chuyển tất cả các từ theo title case, như là chuyển "this is a Title" thàh "This Is
A Title".
- Chuyển những từ dài hơn 3 kí tự thành title case , ví dụ chuyển "this is a Title" to “This is a Title” - Bảo đảm các câu được viết hoa - Phân cách những phần tử của URL ( ví dụ cho http://www.wrox.com), chi tiết giao thức tên máy, tên file ,..) mặc dù có thể sử dụng các phương thức System.String và System.Text.StringBuilder để làm các việc trên nhưng nếu dùng biểu thức chính quy thì mã có thể được giảm xuống còn vài dòng.ta khởi tạo 1 đối tượng
System.Text.RegularExpressions.RegEx , truyền vào nó chuỗi được xử lí, và 1 biểu
thức chính quy ( 1 chuỗi chứa đựng các lệnh trong ngôn ngữ biểu thức chính quy ). 1 chuỗi biểu thức chính quy nhìn giống 1 chuỗi bình thường nhưng có thêm 1 số chuỗi
83 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
hoặc kí tự khác làm cho nó có ý nghĩa đặc biệt hơn. Ví dụ chuỗi \b chỉ định việc bắt
đầu hay kết thúc 1 từ, vì thế nếu ta muốn chỉ định tìm kí tự th bắt đầu 1 từ, ta có thể
tìm theo biểu thức chính quy ,\b th. Nếu muốn tìm tất cả sự xuất hiện của th ở cuối từ
ta viết th\b. Tuy nhiên, biểu thức chính quy có thể phức tạp hơn thế, ví dụ điều kiện để lưu trữ phần kí tự mà tìm thấy bởi thao tác tìm kiếm.
1 ví dụ khác giả sử như ta muốn chuyển 1 số diện thoại UK từ trong nước sang
định dạng quốc tế. trong UK, định dạng ví dụ như là 01233 345532 hoặc (01233
345532) mà theo quốc tế sẽ là +44 12330345532, nói cách khác số 0 đầu sẽ được thay bằng +44 và các dấu ngặc phải được bỏ. Thao tác này không quá phức tạp, nhưng
cũng hơi rắc rối nếu ta dùng lớp chuỗi để làm ( nghĩa là dùng các phương thức trong
lớp chuỗi). ngôn ngữ biểu thức chính quy sẽ cho phép ta xây dựng 1 chuỗi ngắn mà sẽ
được phiên dịch để đạt được yêu cầu trên.
Ví dụ RegularExpressionsPlayaround
Trong phần cuối của phần này ta sẽ phát triển 1 ví dụ ngắn thể hiện vài đặc tính
của biểu thức chính quy và cách dùng biểu thức chính quy trong C# bằng biệc trình
bày và biểu diễn kết quả của việc tìm kiếm.ta dùng 1 đoạn văn bản trong cuốn sách
XML cho ví dụ của ta :
string Text = @"XML has made a major impact in almost every aspect of
software development Designed as an open, extensible, self-describing language, it has become the standard for data and document delivery on the
web. The panoply of XML-related technologies continues to develop at
breakneck speed, to enable validation, navigation, transformation, linking,
querying, description, and messaging of data.";
Ta xem đoạn văn bản này là chuỗi input.giả sử ta muốn tìm tất cả các lần xuất
hiện của ion. ta sẽ viết như sau:
string Pattern = "ion";
MatchCollection Matches = Regex.Matches(Text, Pattern,
RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); foreach (Match NextMatch in Matches) { Console.WriteLine(NextMatch.Index); } Trong ví dụ này ta dùng phương thức tĩnh Matches() của lớp Regex trong
namespace System.Text.RegularExpressions. Phương thức này có thông số là text,
pattern, và tập cờ từ cấu trúc liệt kê RegexOptions.trong trường hợp này ta chỉ định tìm kiếm không phân biệt chữ hoa - thường. và cờ ExplicitCapture, cập nhật cách mà
84 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
match được thu thập ta sẽ thấy tại sao hàm Matches() trả về 1 tham chiếu đến đối
tượng MatchCollection. 1 match là 1 thuật ngữ kĩ thuật cho những kết quả của việc tìm
1 thể hiện của pattern trong biểu thức. Được trình bày bởi lớp
System.Text.RegularExpressions.Match do đó ta sẽ trả về 1 MatchCollection chứa tất cả các match, mỗi cái đưọc trình bày bởi một đối tượng Match.trong đoạn mã trên, ta
đơn giản lặp trên tập thu được và dùng thuộc tính index của lớp Match, mà trả về chỉ
mục trong đoạn input nơi mà match được tìm thấy.khi chạy nó sẽ tìm ra 4 match.
Đến bây giờ ta vẫn chưa thấy gì thật sự mới ở đây .tuy nhiên sức mạnh của biểu thức chính quy nằm ở chuỗi pattern. lý do là chuỗi pattern không chỉ chứa văn bản kí tự.nó
còn có thể chứa metacharacters, là những kí tự đặc biệt mà dùng trong lệnh, những
chuỗi escape, làm việc giống như các chuỗi escape của C# là những kí tự đưọc bắt đầu
bằng dấu \, và cũng có ý nghĩa đặc biệt.
Ví dụ , ta muốn tìm từ bắt đầu vớ n. ta có thể dùng chuỗi \b, mà chỉ định 1 ranh
giới từ ta đơn giản viết:
string Pattern = @"\bn";
MatchCollection Matches = Regex.Matches(Text, Pattern,
RegexOptions.IgnoreCase |
RegexOptions.ExplicitCapture);
Chú ý kí tự @ đứng trước chuỗi.nghĩa là ta muốn \b được truyền vào biểu thức chính quy .NET vào lúc chạy- ta không muốn dấu \ bị chặn bởi trình biên dịch của C#
mà xem nó như là chuỗi escape của nó.nếu ta muốn tìm từ kết thúc với ion, ta viết:
string Pattern = @"ion\b";
Nếu ta muốn tìm tất cả những từ bắt đầu bằng n và kết thúc với ion, rõ ràng ta
cần 1 pattern bắt đầu với \bn và kết thúc với ion\b vậy chính giữa sẽ là gì ? Ta cần 1
cái gì đó để cho ứng dụng biết giữa n và ion có thể là bất cứ từ gì không phải khoảng
trắng. ta viết :
string Pattern = @"\bn\S*ion\b";
\S chỉ định bất kì kí tự nào không phải khoảng trắng , * được gọi là quantifier. nghĩa là kí tự đứng trước có thể lặp nhiều lần , kể cả 0 lần. chuỗi \s* nghĩa là “bất kì kí tự nào không phải là khoảng trắng”. Bảng sau thể hiên một số kí tự hoặc chuỗi escape mà ta có thể dùng
Ý nghĩa Ví dụ
Examples that this will match
^ Bắt đầu của chuổi nhập ^B B, nhưng chỉ nếu kí tự đầu
tiên trong chuỗi
$ Kết thúc của chuỗi nhập X$ X, nhưng chỉ nếu kí tự cuối
85 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
cùng trong chuỗi
. Bất kì kí tự nào ngoại trừ kí tự i.ation isation, ization
xuống dòng(\n)
* ra*t rt, rat, raat, raaat, and so on
Kí tự trước có thể được lặp lại 0 hoặc nhiều lần
+ Kí tự trước có thể được lặp lại 1 ra+t rat, raat, raaat and so on,
hoặc nhiều lần (but not rt)
? ra?t rt and rat only
Kí tự trước có thể được lặp lại 0 hoặc 1 lần
\s Bất kì kí tự khoảng trắng \sa
[space]a, \ta, \na (\t and \n có ý nghĩa giống như trong C#)
\S Bất kì kí tự nào không phải là \SF aF, rF, cF, but not \tf
khoảng trắng
\b Từ biên ion\b any word ending in ion
\B bất kì vị trí nào không phải là từ \BX\B bất kì kí tự X ở giữa của 1
biên từ
Nếu ta thực sự muốn tìm 1 kí tự metacharacters , ta có thể làm bằng cách thêm
vào kí tự tương ứng với dấu \ ví dụ . ( dấu chấm ) nghĩa là bất kì kí tự đơn nào ngoại
trừ 1 kí tự xuống dòng, trong khi \ . nghĩa là 1 dấu chấm. ta có thể yêu cầu 1 match
chứa đựng 1 kí tự thay thế bằng cách bỏ chúng trong dấu ngoặc. Ví dụ [1|c] nghĩa là 1
kí tự mà có thể là 1 hoặc c. nếu muốn tìm bất kì từ nào là map or man , ta dùng chuỗi
ma[n|p].ta có thể kí hiệu 1 vùng ví dụ [1-z] để chỉ định kí tự thường , [A-E] chỉ định bất kì chữ hoa nào giữa A và E hoặc [0-9] trình bày 1 số đơn. Nếu ta muốn tìm 1 số
integer ( 1 chuỗi chỉ chứa các số từ 0đến 9 ) ta viết [0-9]+ . dấu + chỉ định phải rằng
phả có ít nhất 1 kí số.
Trình bày kết quả
Trong phần này ta sẽ xét ví dụ RegularExpressionsPlayaround để ta thiết lập 1 vài biểu thức chính quy và trình bày kết quả để thấy cách mà biểu thức chính quy làm việc tâm điểm là phương thức WriteMatches(), mà trình bày tất cả các match từ MatchCollection theo định dạng chi tiết hơn. Trong mỗi match, nó trình bày chỉ mục nơi mà match được tìm thấy trong chuỗi nhập,chuỗi của match bao gồm match cộng
thêm 19 kí tự bao quanh nó trong chuỗi nhập - 5 kí tự đứng trước và 5 kí tự đứng sau
(nhỏ hơn 5 kí tự nếu match xuất hiện trong 5 kí tự của phần đầu và kết thúc của đoạn nhập). Ví dụ match trên từ messaging mà xuất hiện gần cuối của chuỗi nhập được
86 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
đánh dấu sẽ trình bày “and messaging of d” (5 kí tự trước và sau match) nhưng 1
match trên từ cuối data sẽ trình bày “g of data.” ( chỉ 1 kí tự sau match). Bởi vì sao đó
là cuối chuỗi 1 chuỗi dài hơn để ta thấy rõ nơi biểu thức chính quy định vị match:
static void WriteMatches(string text, MatchCollection matches) {
Console.WriteLine("Original text was: \n\n" + text + "\n");
Console.WriteLine("No. of matches: " + matches.Count);
foreach (Match nextMatch in matches) {
int Index = nextMatch.Index;
string result = nextMatch.ToString();
int charsBefore = (Index < 5) ? Index : 5; int fromEnd = text.Length - Index - result.Length;
int charsAfter = (fromEnd < 5) ? fromEnd : 5;
int charsToDisplay = charsBefore + charsAfter + result.Length;
Console.WriteLine("Index: {0}, \tString: {1}, \t{2}",
Index, result,
text.Substring(Index - charsBefore, charsToDisplay)); }
}
Phần lớn của quy trình trong phương thức này minh hoạ số kí tự đượctrình bày
trong chuỗi con dài hơn mà nó có thể trình bày không quan tâm đến đầu hay cuối
chuỗi.lưu ý ta sử dụng 1 thuộc tính khác của đối tượng Match , Value, chứa chuỗi xác
định trong Match.RegularExpressionsPlayaround chứa 1 số phương thức với tên như
là Find1, Find2 mà biểu diễn việc tìm kiếm dựa trên ví dụ trong phần này. Ví dụ find2
tìm bất kì chuỗi chứa n vào lúc đầu của 1 từ :
static void Find2() { string text = @"XML has made a major impact in almost every aspect of software development. Designed as an open, extensible, self-describing language, it has become the standard for data and document delivery on the web. The panoply of XML-related technologies continues to develop at breakneck speed, to enable validation, navigation, transformation,
linking, querying, description, and messaging of data.";
string pattern = @"\bn"; MatchCollection matches = Regex.Matches(text, pattern,
87 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
RegexOptions.IgnoreCase);
WriteMatches(text, matches);
}
Cùng với phương thức này là một phương thức main() mà ta có thể chỉnh sửa
đề chọn 1 trong những phương thức Find
static void Main()
{ Find1();
Console.ReadLine();
}
3.3.5 Nhóm các đối tượng
Chúng ta đã khảo sát 1 số lớp cơ sở của .NET có cấu trúc dữ liệu trong đó một
số đối tượng được nhóm với nhau.cấu trúc đơn giản mà ta đã học là mảng, đây là 1 thể
hiện của lớp System.Array . mảng có lợi điểm là ta có thể truy nhập từng phần tử
thông qua chỉ mục.tuy nhiên khuyết điểm của nó là ta phải khởi tạo kích thước của nó.
không thể thêm ,chèn hoặc bỏ 1 phần tử sau đó.và phải có một chỉ mục số để truy nhập
vào 1 phần tử.điều này không tiện lắm ví dụ như khi ta làm việc với 1 bản ghi nhân
viên và muốn tìm bản ghi theo tên nhân viên. .NET có một số cấu trúc dữ liệu khác hổ trợ cho công việc này.ngoài ra còn có 1 số inteface , mà các lớp có thể khai báo chúng
hổ trợ tất cả chức năng của một kiểu cụ thể cấu trúc dữ liệu. chúng ta sẽ xem xét 3 cấu
trúc sau (các lớp cấu trúc dữ liệu này nằm trong namespace System.Collection):
-Array lists
- Collection
- Dictionary ( hay maps)
a. Array lists
Array list giống như mảng, ngoại trừ nó có khả năng phát triển.được đại diện
bởi lớp System.Collection.Arraylist. Lớp Arraylist cũng có một một vài điểm tương tự với lớp StringBuilder mà ta tìm hiểu trưóc đây.như StringBuilder cấp phát đủ chỗ trống trong vùng nhớ để lưu trữ 1 số kí tự, và cho phép ta thao tác các kí tự trong chỗ trống đó, Arraylist cấp đủ vùng nhớ để lưu trữ 1 số các tham chiếu đối tượng. ta có thể thao tác trên những tham chiếu đối tượng này.nếu ta thử thêm một đối tượng đến Arraylist hơn dung lượng cho phép của nó, thì nó sẽ tự động tăng dung lượng bằng cách cấp phát thêm vùng nhớ mới lớn đủ để giữ gấp 2 lần số phần tử của dung lượng
hiện thời.Ta có thể khởi tạo 1 danh sách bằng cách chỉ định dung lượng ta muốn. Các
lớp cấu trúc dữ liệu này nằm trong namespace System.Collection. Ví dụ , ta tạo ra một danh sách Vectors:
88 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
ArrayList vectors = new ArrayList(20);
Nếu ta không chỉ định kích cỡ ban đầu , mặc định sẽ là 16:
ArrayList vectors = new ArrayList(); // kích cỡ là 16
Ta có thể thêm phần tử bằng cách dùng phương thức Add():
vectors.Add(new Vector(2,2,2));
vectors.Add(new Vector(3,5,6));
Arraylist xem tất cả các phần tử của nó như là các tham chiếu đối tượng..nghĩa là ta có thể lưu trữ bất kì đối tượng nào mà ta muốn trong 1 Arraylist. nhưng khi truy
nhập đến đối tượng, ta sẽ cần ép kiểu chúng trở lại kiểu dữ liệu tương đương:
Vector element1 = (Vector)vectors[1];
Ví dụ này cũng chỉ ra Arraylist định nghĩa 1 indexer, để ta có thể truy nhập những phần tử của nó với cấu trúc như mảng. ta cũng có thể chèn các phần tử vào
array list:
vectors.Insert(1, new Vector(3,2,2)); // chèn vào vị trí 1
Đây là phương thức nạp chồng có ích khi ta muốn chèn tất cả các phần tử trong
1 collection vào arraylist ta có thể bỏ 1 phần tử :
vectors.RemoveAt(1); // bỏ đối tượng ở vị trí 1
Ta cũng có thể cung cấp 1 đối tượng tham chiếu đến 1 phương thức khác, Remove().nhưng làm điều này sẽ mất nhiều thời gian hơn vì arraylist phải quét qua
toàn bộ mảng để tìm đối tượng.
Lưu ý rằng việc thêm và bỏ 1 phần tử sẽ làm cho tất cả các phần tử theo sau
phải bị thay đổi tương ứng trong bộ nhớ, thậm chí nếu cần thì có thể tái định vị toàn bộ
Arraylist. Ta có thể cập nhật hoặc đọc dung lượng qua thuộc tính :
vectors.Capacity = 30;
Tuy nhiên việc thay đổi dung lương đó sẽ làm cho toàn bộ Arraylist được tái
định vị đến một khối bộ nhớ mới với dung lượng đưọc yêu cầu.
Để biết số phần tử thực sự trong arraylist ta dùng thuộc tính Count:
int nVectors = vectors.Count; 1 arraylist có thể thực sự hữu ích nếu ta cần xây dựng 1 mảng đối tuợng mà ta không biết kích cỡ của mảng sẽ là bao nhiêu. trong trường hợp đó, ta có thể xây dựng ' mảng' trong Arraylist, sau đó sao chép Arraylist trở lại mảng khi ta hoàn thành xong nếu ta thực sự cần dữ liệu như là 1 mảng ( ví dụ nếu mảng được truyền đến 1 phương
thức xem mảng là 1 thông số). mối quan hệ giữa Arraylist và Array theo 1 cách nào đó
giống như mối quan hệ giữa StringBUilder và String. Không như lớp StringBuilder, không có phương thức đơn nào để làm việc chuyển đổi từ 1 arraylist sang array. Ta
89 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
phải dùng 1 vòng lặp để sao chép thủ công trở lại.tuy nhiên ta chỉ phải sao chép tham
chiếu chứ không phải đối tượng:
// vectors is an ArrayList instance being used to store Vector instances Vector [] vectorsArray = new Vector[vectors.Count];
for (int i=0 ; i< vectors.Count ; i++)
vectorsArray[i] = (Vector)vectors [i];
b. Collections
Ý tưởng của Collection là nó trình bày một tập các đối tượng mà ta có thể truy
xuất bằng việc bước qua từng phần tử. cụ thể là 1 tập đối tượng mà ta có thể truy nhập
sử dụng vòng lặp foreach. nói cách khác ,khi viết 1 thứ gì đó như:
foreach (string nextMessage in messageSet) {
DoSomething(nextMessage);
}
Ta xem biến messageSet là 1 collection . khả năng để dùng vòng lặp foreach là
mục đích chính của collection tiếp theo ta tìm hiểu chi tiết collection là gì và thi hành 1
collection riêng bằng việc chuyển ví dụ Vector mà ta đã phát triển.
Collection là gì ?
1 đối tượng là 1 collection nếu nó có thể cung cấp 1 tham chiếu đến một đối
tượng có liên quan, được biết đến như là enumarator, mà có thể duyệt qua từng mục
trong collection. đặc biệt hơn, 1 collection phải thi hành 1 interface
System.Collections.IEnumerable. IEnumerable định nghĩa chỉ một phương
thức như sau:
interface IEnumerable
{
IEnumerator GetEnumerator();
} Mục đích của GetEnumarator() là để trả về đối tuợng enumarator. khi ta tập họp những đoạn mã trên đối tượng enumarator được mong đợi để thi hành 1 interface , System.Collections.IEnumerator.
Ngoài ra còn có một interface khác , Icollection , đưọc dẫn xuất từ IEnumerable. những collection phức tạp hơn sẽ thi hành interface này.bên cạnh GetEnumerator(), nó thi hành một thuộc tính trả về trực tiếp số phần tử trong
collection. nó cũng có đặc tính hổ trợ việc sao chép collection đến 1 mảng và có thể
cung cấp thông tin đặc tả nếu đó là một luồng an toàn.tuy nhiên trong phần này ta chỉ xem xét interface IEnumerable. IEnumarator có cấu trúc sau:
90 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
interface IEnumerator
{
object Current { get; }
bool MoveNext(); void Reset();
}
IEnumarator làm việc như sau: đối tuợng thực thi nên được kết hợp với 1
collection cụ thể. khi đối tượng này được khởi động lần đầu tiên,nó chưa trỏ đến bất kì 1 phần tử nào trong collection, và ta phải gọi MoveNext(), mà sẽ di chuyển
enumarator để nó chuyển đến phần tử đầu tiên trong collection. ta có thể nhận phần tử
này với thuộc tính Current.Current trả về 1 tham chiếu đối tượng , vì thế ta sẽ ép kiểu
nó về kiểu đối tượng mà ta muốn tìm trong Collection.ta có thể làm bất cứ điều gì ta muốn với đối tượng đó sau đó di chuyển đến mục tiếp theo trong collection bằng cách
gọi MoveNext() lần nữa.ta lập lại cho đến khi hết mục trong collection- khi current trả
về null.nếu muốn ta có thể quay trở về vị trí đầu trong collection bằng cách gọi
Reset(). lưu ý rằng Reset() thực sự trả về trước khi bắt đầu collection , vì thế nếu muốn
di chuyển đến phần tử đầu tiên ta phải gọi MoveNext(). 1 collection là 1 kiểu cơ bản
của nhóm đối tượng.bởi vì nó không cho phép ta thêm hoặc bỏ mục trong nhóm.tất cả
ta có thể làm là nhận các mục theo 1 thứ tự được quyết định bởi collection.và kiểm tra chúng.thậm chí ta không thể thay thế hoặc cập nhật mục vì thuộc tính current là chỉ
đọc.hầu như cách dùng thường nhất của collection là cho ta sự thuận tiện trong cú
pháp của lặp foreach. Mảng cũng là 1 collection,nhưng lệnh foreach làm việc tốt hơn
mảng.
Ta có thể xem vòng lặp foreach trong C# là cú pháp ngắn trong việc viết:
{
IEnumerator enumerator = MessageSet.GetEnumerator();
string nextMessage;
enumerator.MoveNext(); while ( (nextMessage = enumerator.Current) != null) { DoSomething(nextMessage); // NB. We only have read access // toNextMessage enumerator.MoveNext(); }
}
91 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
1 khía cạnh quan trọng của collection là bộ đếm được trả về như là 1 đối tượng
riêng biệt.lý do là để cho phép khả năng có nhiều hơn 1 bộ đếm có thể áp dụng đồng
thời trong cùng collection.
Thêm collection hỗ trợ cấu trúc Vector
Trong lần cuối cùng ta nói về Vector , một thể hiện của Vector chứa đựng 3
phần, x,y,z và bởi vì ta đã định nghĩa 1 bộ chỉ mục ở chương 3, nó có thể đuợc xem 1
thể hiện Vector là 1 mảng , để ta có thể truy nhập vào phần x bằng cách viết
someVector[0], phần y bằng cách viết someVecor[1] và z là someVector[2]. Bây giờ ta sẽ mở rộng cấu trúc vector, dự án VectorAsCollection mà cũng có thể quét
qua các phần của 1 vector bằng cách viết :
foreach (double component in someVector)
Console.WriteLine("Component is " + component); Nhiệm vụ đầu tiên của ta là biểu thị vector như là 1 collection bằng việc cho nó
thực thi interface IEnumerable, ta bắt đầu bằng việc cập nhật khai báo của cấu trúc
vector:
struct Vector : IFormattable, IEnumerable
{
public double x, y, z;
Bây giờ ta thi hành interface IEnumerable : public IEnumerator GetEnumerator()
{
return new VectorEnumerator(this);
}
Việc thi hành GetEnumerator() hầu như là đơn giản, nhưng nó tuỳ thuộc trên sự
tồn tại của 1 lớp mới, VectorEnumerator,mà ta cần định nghĩa. vì VectorEnumerator
không phải là 1 lớp mà bất kì đoạn mã bên ngoài có thể thấy trực tiếp, ta khai báo nó
là lớp private bên trong cấu trúc Vector. việc định nghĩa nó như sau:
private class VectorEnumerator : IEnumerator { Vector theVector; // Vector object that this enumerato refers to int location; // which element of theVector the enumerator is // currently referring to public VectorEnumerator(Vector theVector)
{
this.theVector = theVector; location = -1;
92 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
}
public bool MoveNext()
{ ++location;
return (location > 2) ? false : true;
}
public object Current
{
get
{ if (location < 0 || location > 2)
throw new InvalidOperationException(
"The enumerator is either before the first element or " +
"after the last element of the Vector");
return theVector[(uint)location];
}
}
public void Reset()
{
location = -1;
}
}
Khi được yêu cầu như 1 bộ đếm, VectorEnumerator thi hành interface
IEnumerator. nó cũng chứa 2 trường thành viên, theVector,1 tham chiếu đến Vector (
collection) mà bộ đếm kết hợp, location, 1 số nguyên mà chỉ định nơi trong collection mà bộ đếm tham chiếu đến. Cách làm việc là xem location như là chỉ mục và thi hành enumerator để truy nhập Vector như mảng.khi truy nhập vector như mảng giá trị chỉ mục là 0,1,2 - tamở rộng bằng cách dùng -1 như là giá trị chỉ định bộ đếm trước khi bắt đầu collection,và 3 để chỉ nó đến cuối của collection. vì vậy , việc khởi tạo của trường nay là -1 trong hàm dựng VectorEnumerator:
public VectorEnumerator(Vector theVector)
{
this.theVector = theVector;
93 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
location = -1;
}
Lưu ý rằng hàm dựng cũng lấy 1 tham chiếu đến thể hiện của Vector mà chúng ta định
đếm - điều này được cung cấp trong phương thức Vector.GetEnumerator :
public IEnumerator GetEnumerator()
{
return new VectorEnumerator(this);
}
3.3.6 Dictionaries
Từ điển trình bày 1 cấu trúc dữ liệu rất phức tạp mà cho phép ta truy nhập vào
các phần tử dựa trên 1 khoá nào đó, mà có thể là kiểu dữ liệu bất kì.ta hay gọi là bảng
ánh xạ hay bảng băm.Từ điển được dùng khi ta muốn lưu trữ dữ liệu như mảng nhưng muốn dùng 1 kiểu dữ liệu nào đó thay cho kiểu dữ liệu số làm chỉ mục.nó cũng cho
phép ta thêm hoặc bỏ các mục , hơi giống danh sách mảng tuy nhiên nó không phải
dịch chuyển các mục phía sau trong bộ nhớ. Ta minh họa việc dùng từ điển trong ví dụ
sau: MortimerPhonesEmployees. Trong ví dụ này công ty điện thoại có vài phần mềm
xử lí chi tiết nhân viên .ta cần 1 cấu trúc dữ liệu -hơi giống mảng- mà chứa dữ liệu của
nhân viên. Ta giả sử rằng mỗi nhân viên trong công ty được xác định bởi I nhân
viên, là tập kí tự như B342… và được lưu trữ thành đối tượng EmployyeeI . Chi tiết của nhân viên được lưu trữ thành đối tượng Employee ata, ví dụ chỉ chứa I ,tên,
lương của nhân viên. Giả sử ta có EmployeeI :
EmployeeID id = new EmployeeID("W435")
và ta có 1 biến gọi là employees, mà ta có thể xem như 1 mảng đối tượng
Employee ata.thực sự , nó không phải là mảng - nó là từ điển và bởi vì nó là từ điển
nên ta có thể lấy chi tiết của 1 nhân viên thông qua I đuợc khai báo trên:
EmployeeData theEmployee = employees[id];
// lưu ý rằng ID không phải kiểu số- nó là 1 thể hiện của EmployeeID
Đó là sức mạnh của từ điển.Ta có thể dùng kiểu dữ liệu bất kì làm chỉ mục , lúc này ta gọi nó là khoá chứ không phải là chỉ mục nữa.khi ta cung cấp 1 khoá truy nhập vào 1 phần tử ( như I trên ), nó sẽ xử lí trên giá trị của khoá và trả về 1 số nguyên tuỳ thuộc vào khoá, và được dùng để truy nhập vào 'mảng' để lấy dữ liệu.
Từ điển trong .NET
Trong .NET , từ điển cơ bản được trình bày qua lớp Hasthable, mà cách làm việc cũng giống như từ điển thực, ngoại trừ nó xem khoá và mục có kiểu object.nghĩa
là 1 bảng băm có thể lưu trữ bất kì cấu trúc dữ liệu nào ta muốn.
ta có thể tự định nghĩa 1 lớp từ điển riêng cụ thể hơn.Microsoft cung cấp 1 lớp cơ sở trừu tượng, ictionaryBase,cung cấp những chức năng cơ bản của từ điển ,mà ta có thể
94 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
dẫn xuất đến lớp mà ta muốn tạo.nếu khoá là chuỗi ta có thể dùng lớp
System.Collections.Specialized.StringDictionary thay cho Hasthable.
khi tạo một Hasthable ta có thể chỉ định kích thước khởi tạo của nó:
Hasthable employees = new Hasthable(53); Ở đây ta chọn số 53 bởi vì thuật toán bên trong được dùng cho từ điển làm việc
hiệu quả hơn nếu kích thước của nó là 1 số nguyên tố.
Thêm đối tượng vào từ điển ta dùng phương thức Add(), có 2 thông số kiểu object :
thông số đầu là khoá, thứ hai là 1 tham chiếu đến dữ liệu. ví dụ:
EmployeeID id;
EmployeeData data;
// khởi tạo id và dữ liệu. // giả sử employees là 1 thể hiện của bảng băm
//mà chứa đựng các tham chiếu EmployeeData
employees.Add(id, data);
để nhận dữ liệu ta cung cấp khoá cho nó:
EmployeeData data = employees[id];
để bỏ 1 mục ta cung cấp khoá và gọi :
employees.Remove(id);
Để đếm số mục trong từ điển ta dùng thuộc tính Count:
int nEmployees = employees.Count;
Việc lưu trữ trong từ điển không theo phải theo kiểu từ trên xuống, nghĩa là ta
không thể tìm thấy 1 khối lớn dữ liệu ở phần đầu của cấu trúc và 1 khối rỗng ở phần
cuối. biểu đồ sau minh hoạ cho việc lưu trữ trong từ điển, các phần không đánh dấu là
rỗng:
Cách từ điển làm việc
95 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Hasthable (hay bất kì lớp từ điển nào khác) sử dụng vài thuật toán để thực hiện
việc đặt mỗi đối tượng dựa trên khoá. có 2 giai đoạn, và phần mã cho từng giai đoạn
phải được cung cấp bởi lớp khoá.nếu sử dụng lớp do Microsoft viết, mà dùng làm
khoá (như chuỗi), thì không có vấn đề gì (Microsoft đã viết sẵn rồi). Nhưng nếu lớp khoá do ta viết thì ta phải tự viết phần thuật toán này. 1 phần của thuật toán thực thi
bởi lớp khoá gọi là băm (vì vậy có thuật ngữ bảng băm) và lớp Hasthable tìm 1 nơi cụ
thể cho thuật toán băm, nhìn vào phương thức Gethashcode() trong đối tượng của ta,
mà thừa kế từ System.Object() nếu ta nạp chồng GetHashCode().
Cách nó làm việc là Gethashcode() trả vế 1 số nguyên.bằng cách nào đó nó
dùng giá trị của khoá để sinh ra 1 số nguyên.Hasthable sẽ lấy số nguyên này và làm
các việc xử lí khác trên nó mà liên quan đến việc tính toán toán học phức tạp,và trả về
chỉ mục của mục đưọc lưu trữ tương ứng với khóa trong từ điển.ta không đi sâu vào thuật toán này nhưng ta sẽ tìm hiểu tại sao nó liên quan đến số nguyên tố và tại sao
dung lượng bảng băm nên là số nguyên tố.
Có một số yêu cầu nghiêm ngặt khi ta nạp chồng GetHashCode(). Những yêu
cầu này nghe có vẻ trừu tượng nhưng qua ví dụ MortimerPhonesEmployees ta sẽ thấy
rằng không quá khó để viết lớp khoá thỏa mãn những đòi hỏi sau:
- Nó phải nhanh ( bởi vì việc đặt và lấy các mục trong 1 từ điển được coi là
nhanh)
- Nó phải được đồng nhất - nếu ta cho 2 khoá cùng giá trị thì chúng phải cho
cùng giá trị trong băm.
- Cho những giá trị khả dĩ trong khoảng giá trị của 1 số kiểu int.
Lí do của điều kiện cuối là : điều gì sẽ xảy ra nếu ta lấy 2 mục trong từ điển mà
khi băm cả hai đều cho cùng 1 chỉ mục? Nếu điều này xảy ra, lớp từ điển sẽ phải bắt
đầu tìm kiếm vị trí trống có giá trị gần nhất để lưu trữ mục thứ hai. Xung đột giữa các
khóa cũng gia tăng khi từ điển đầy,vì thế cách tốt nhất là bảo đảm dung lượng lớn hơn
số phần tử thực sự trong nó.vì lí do này mà Hasthable tự định vị lại kích cỡ của nó để
tăng dung lượng trước khi nó đầy.tỷ lệ của bảng mà đầy gọi là load. ta có thể thiết lập giá trị lớn nhất mà ta muốn load đến trước khi Hasthable tái định vị theo hàm dựng Hasthable khác :
// dung lượng =50, Max Load = 0.5 Hasthable employees = new Hasthable(50, 0.5); Max load càng nhỏ bảng băm làm việc càng hiệu quả nhưng càng cần nhiều vùng nhớ.khi bảng băm tái định vị để tăng dung lượng , nó luôn chọn 1 số nguyên tố
làm dung lượng mới. 1 điểm quan trọng khác là thuật toán băm phải đồng nhất.nếu 2
đối tượng chứa những gì ta coi như là dữ liệu trùng, thì chúng phải cho cùng 1 giá trị băm, và điều này dẫn đến 1 giới hạn quan trọng trên cách nạp chồng phương thức
96 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Equals() và Gethashcode() của System.Object. cách mà Hasthable quyết định 2 khoá a
và b là bằng nhau là nó gọi a.equals(b). nghĩa là ta phải chắc rằng điều sau luôn đúng :
Nếu a.equals(b) là đúng thì a.gethashcode() và b.gethashcode() phải luôn trả về cùng
mã băm. Nếu ta cố ý nạp chồng những phương thức này để những câu lệnh trên không đúng thì bảng băm sẽ không làm việc bình thường. ví dụ như ta đặt 1 đối tượng vào
bảng băm nhưng không nhận lại được nó hay nhận lại được nhưng không đúng mục.
trong system.object điều kiện này đúng , vì Equals() đơn giản so sánh 2 tham chiếu và
gethashcode() thực sự trả về 1 băm dựa trên địa chỉ của đối tượng.nghĩa là bảng băm dựa trên 1 khoá mà không nạp chồng những phương thức này sẽ làm việc đúng.tuy
nhiên ,vấn đề với cách làm này là những khóa coi là bằng chỉ nếu chúng là cùng đối
tượng.nghĩa là khi đặt 1 đối tượng vào từ điển ta phải nối tham chiếu đến khóa.ta
không thể khởi tạo 1 khóa khác sau đó mà có cùng giá trị,vì cùng giá trị được định nghĩa theo nghĩa là cùng một thực thể. nghĩa là nếu ta không nạp chồng bản object của
Equals() và Gethashcode(), lớp của ta sẽ không thuận lợi để dùng trong bảng băm. tốt
hơn nếu thi hành gethashcode() sinh ra 1 băm dựa trên giá trị của khoá hơn là điạ chỉ
của nó trong bộ nhớ.do đó ta sẽ cần nạp chồng gethashcode() va equals() trong bất kì
lớp nào mà ta muốn nó được sử dụng như khoá System.String có những phương thức
nạp chồng tương đương, Equals() được nạp chồng để cung cấp giá trị so sánh, và
gethashcode() được nạp chồng để trả về 1 băm dựa trên giá trị của chuỗi.vì lí do này thuận lợi để dùng chuỗi như là khoá trong từ điển.
Ví dụ
Đây là chương trình thiết lập từ điển nhân viên.chương trình khởi tạo từ điển ,
thêm vài nhân viên và sau đó mời người dùng gõ vào Id nhân viên. mỗi khi gõ ,
chương trình dùng I để trỏ vào tử điển và nhận chi tiết nhân ivên. quy trình lặp lại
cho đến khi người dùng gõ X :
MortimerPhonesEmployees
Enter employee ID (format:A999, X to exit)> B001
Employee: B001: Mortimer £100,000.00 Enter employee ID (format:A999, X to exit)> W234 Employee: W234: Arabel Jones £10,000.00 Enter employee ID (format:A999, X to exit)> X Các lớp của chương trình :
class EmployeeID
{ private readonly char prefix;
97 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
private readonly int number;
public EmployeeID(string id) {
prefix = (id.ToUpper())[0];
number = int.Parse(id.Substring(1,3));
}
public override string ToString()
{ return prefix.ToString() + string.Format("{0,3:000}", number);
}
public override int GetHashCode()
{
return ToString().GetHashCode();
}
public override bool Equals(object obj)
{
EmployeeID rhs = obj as EmployeeID;
if (rhs == null)
return false;
if (prefix == rhs.prefix && number == rhs.number)
return true;
return false; } } Phần định nghĩa đầu tiên của lớp lưu trữ I .bao gồm 1 kí tự chữ đứng đầu theo sau là 3 kí tự số. ta dùng kiểu char để lưu chữ đầu và int để lưu phần sau. Hàm dựng nhận 1 chuỗi và ngắt nó thành những trường này.phuơng thức Tostring() trả về I là chuỗi:
return prefix.ToString() + string.Format("{0,3:000}", number);
98 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Phần đặc tả định dạng (0,3:000) để phần int chứa số được điền thêm số 0 ví dụ
ta sẽ có B001 không phải B1 ta đến 2 phương thức nạp chồng trong từ điển :
Đầu tiên là Equals() để so sánh giá trị của những thể hiện EmployeeI :
public override bool Equals(object obj) {
EmployeeID rhs = obj as EmployeeID;
if (rhs == null)
return false; if (prefix == rhs.prefix && number == rhs.number)
return true;
return false;
} }
Đầu tiên ta kiểm tra xem đối tượng trong thông số có phải là 1 thể hiện của
EmployeeI không bằng cách thử ép kiểu nó thành đối tượng EmployeeI . sau đó ta
chỉ việc so sánh những trường giá trị của nó có chứa cùng giá trị như đối tuợng này
không. Tiếp theo là Gethashcode() :
public override int GetHashCode()
{ string str = this.ToString();
return str.GetHashCode();
}
Phần trên ta đã xem xét các yêu cầu giới hạn mà mã băm được tính phải thỏa
mãn.tất nhiên có những cách để nghĩ ra những thuật toán băm hiệu quả và đơn giản.
nói chung, lấy 1 trường , nhân nó với 1 số nguyên tố lớn,và công những kết quả lại với
nhau là 1 cách tốt. nhưng ta không phải làm những điều đó vì MIcrosoft đã làm toàn
bộ trong lớp String, vì thế ta có thể lợi dụng lớp này để tạo ra số dựa trên nội dung của
chuỗi.nó sẽ thoã mãn tất cả những yêu cầu của mã băm. Chỉ có 1 khuyết điểm khi dùng phương thức này là có vài việc thi hành đã mất kết hợp với việc chuyển đổi lớp EmployeeI thành chuỗi trong phần đầu tiên.nếu không muốn điều này ta sẽ cần thiết kế mã băm riêng thiết kế thuật toán băm là 1 chủ đề phức tạp mà ta không không thể đi sâu trong cuốn sách này.tuy nhiên ta sẽ đưa ra 1 cách đơn giản cho vấn đế này, mà chỉ việc nhân số dựa trên những trường thành phần của lớp với số nguyên tố khác( nhân bởi 1 số nguyên tố khác giúp ta ngăn ngừa sự kết hợp giá trị khác nhau của các trường
từ việc cho cùng mã băm):
public override int GetHashCode() // alternative implementation {
99 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
return (int)prefix*13 + (int)number*53;
}
Ví dụ này sẽ làm việc nhanh hơn Tostring() mà ta dùng ở trên. tuy nhiên khuyết
điểm là mã băm sinh ra bởi các employeeI khác nhau thì không trải rộng trên vùng số kiểu int ( are less likely to be evenly spread across the range of int ).
tiếp theo ta xem lớp chứa dữ liệu nhân viên :
class EmployeeData
{ private string name;
private decimal salary;
private EmployeeID id;
public EmployeeData(EmployeeID id, string name, decimal salary)
{
this.id = id;
this.name = name;
this.salary = salary;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder(id.ToString(), 100);
sb.Append(": ");
sb.Append(string.Format("{0,-20}", name));
sb.Append(" ");
sb.Append(string.Format("{0:C}", salary));
return sb.ToString();
} } Ta dùng đối tượng StringBuilder để sinh ra chuỗi đại diện cho đối tượng
Employeedata. cuối cùng ta viết đoạn mã kiểm tra lớp TestHarness:
class TestHarness {
Hashtable employees = new Hashtable(31);
public void Run()
100 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
{
EmployeeID idMortimer = new EmployeeID("B001");
EmployeeData mortimer = new EmployeeData(idMortimer, "Mortimer",
100000.00M); EmployeeID idArabel = new EmployeeID("W234");
EmployeeData arabel= new EmployeeData(idArabel, "Arabel Jones",
10000.00M);
employees.Add(idMortimer, mortimer);
employees.Add(idArabel, arabel);
while (true) {
try
{
Console.Write("Enter employee ID (format:A999, X to exit)> ");
string userInput = Console.ReadLine();
userInput = userInput.ToUpper();
if (userInput == "X") return;
EmployeeID id = new EmployeeID(userInput);
DisplayData(id);
}
catch (Exception e)
{
Console.WriteLine("Exception occurred. Did you use the correct
format for the employee ID?");
Console.WriteLine(e.Message); Console.WriteLine(); } Console.WriteLine(); } }
private void DisplayData(EmployeeID id) {
101 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
object empobj = employees[id];
if (empobj != null)
{
EmployeeData employee = (EmployeeData)empobj; Console.WriteLine("Employee: " + employee.ToString());
}
else
Console.WriteLine("Employee not found: ID = " + id); }
}
Đầu tiên ta thiết lập dung lượng của từ điển là số nguyên tố, 31, phần chính của
lớp này là phương thức run().đầu tiên là thêm vài nhân viên vào từ điển mortimer và arabel và thêm chi tiết của họ vào:
employees.Add(idMortimer, mortimer);
employees.Add(idArabel, arabel);
Tiếp theo ta bước vào vòng lặp để yêu cầu người dùng nhập vào EmployeeI .
có khối try bên trong vòng lặp, bẫy những lỗi khi người dùng không gõ đúng định
dạng cuả EmployeeID:
string userInput = Console.ReadLine(); userInput = userInput.ToUpper();
if (userInput == "X")
return;
EmployeeID id = new EmployeeID(userInput);
Nếu hàm dựng EployeeI đúng, ta trình bày kết hợp nhân viên bằng cách gọi
, isplay ata(). đây là phương thức mà ta muốn truy nhập vào từ điển với cú pháp
mảng. nhận dữ liệu nhân viên với I là việc đầu tiên trong phương thức này:
private void DisplayData(EmployeeID id)
{ object empobj = employees[id]; Nếu không có nhân viên với I tên , thì employees[id] trả về Null,mà ta sẽ đưa
ra thông báo lỗi nếu ta tìm thấy. nếu không ta ép kiểu tham chiếu empobj thành Employee ata ( nhờ rằng trong từ điển nó lưu đối tượng, vì thế khi nhận lại phần tử từ nó là 1 tham chiếu dối tượng , ta phải ép kiểu tường minh trả về kiểu mà ta đã đặt trong từ điển.) khi ta có tham chiếu EmployeeI , ta trình bày dữ liệu của nó bằng
phương thức Employee ata.ToString() :
EmployeeData employee = (EmployeeData)empobj; Console.WriteLine("Employee: " + employee.ToString());
102 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Ta có phần cuối của mã - phương thức main() kích hoạt ví dụ trên . khởi tạo đối
tượng TestHarness và chạy nó.:
static void Main()
{ TestHarness harness = new TestHarness();
harness.Run();
}
103 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Chương 4: Lập trình winform
4.1 Tìm hiểu các điều khiển cơ bản
Visual Studio .NET Integrated Development Environment (IDE) cung cấp cho
bạn những giao diện chung cho việc phát triển nhiều loại dự án khác nhau trên nền tảng .NET. IDE cho phép khả năng thiết kế giao diện người dùng cho ứng dụng, viết
mã lệnh, biên dịch, và kiểm lỗi cho ứng dụng. Visual Studio .NET cung cấp nhiều
ngôn ngữ để phát triển ứng dụng trong bộ .NET của Microsoft như: Visual Basic,
Visual C#, Visual C++….
Tạo một dự án trong Visual Studio .Net
Chạy ứng dụng Visual Studio 2005 từ thực đơn Start -> Program ->
Microsoft Visual Studio 2005 -> Microsoft Visual Studio 2005. Tạo mới ứng dụng
bằng cách nhấn vào menu File -> New -> Project. Cửa sổ tạo mới Project xuất hiện.
Trong cửa sổ New Project, Project Types pane hiển thị danh mục những kiểu
project mà bạn có thể tạo trong VS. Chúng ta quan tâm tới hai loại project đó là Visual
C# và Setup and Deployment. Trong khi Visual C# là kiểu dự án cho phép tạo ra ứng
dụng bằng ngôn ngữ C#, thì Setup and eployment là kiểu project để triển khai dự án
đến người dùng cuối.
Chọn Visual C# - Windows tại Project Types.
104 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Ở cửa sổ Templates, một số mẫu ứng dụng có sẵn để giúp người phát triển
nhanh chóng tạo ra ứng dụng phù hợp theo yêu cầu. Có các kiểu project template sau
cần chú ý nhất:
- Windows Application: được dùng đẻ tạo những ứng dụng Windows. - Class Library: sử dụng để tạo ra những compnent sử dụng lại trong các dự án
khác
- Windows Controls Library: tạo những công cụ cho môi trường ứng dụng
Window
- Console Application: tạo ra ứng dụng console chạy từ dòng lệnh, giao diện ký
tự.
- Chọn Windows Application
- Đặt tên cho dự án đầu tiên là HelloWindow tại Name - Chọn thư mục lưu trữ dự án tại Location
- Click OK
Màn hình dự án xuất hiện như sau
Chúng ta quan tâm đến những cửa sổ sau: - Window Form: nơi sử dụng để thiết kế giao diện chương trình. - ToolBox: chứa đựng những controls được xây dựng sẵn. Có nhiều tab trong
cửa sổ ToolBox liệt kê những control theo những loại khác nhau.
- Solution Explorer: liệt kê tên dự án, những file thiết kế, mã nguồn, … của
ứng dụng
- Properties Window: cửa sổ chứa đựng những thuộc tính của một control nào
đó, ví dụ Form1 có các thuộc tính như Text, StartPosition, ShowinTaskBar…
105 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
- Erros List: nơi xuất hiển những lỗi của chương trình
Tiếp tục thực hiện ứng dụng…, tại Window Form, click chuột phải, một popup
Menu xuất hiện, trong đó có mục View Code, mục này cho phép xuất hiện cửa sổ soạn
thảo mã lệnh điều khiển chương trình
4.1.1 Form
Một Windows Form là một cửa sổ được xuất hiện trong một ứng dụng. Mỗi
Windows Form là một lớp được kế thừa từ lớp Form nằm trong Namespace
System.Windows.Forms.
Thuộc tính Windows Form Những thuộc tính chung của Windows Form được liệt kê theo bảng sau
Properties Mô tả
Name Là thuộc tính để xác định tên của form, mặc định, thuộc tính
Name của form đầu tiên trong ứng dụng là Form1
Backcolor Thuộc tính xác định màu nền của form
BackgroundImage Thuộc tính xác định hình nền cho form
Font
Thuộc tính xác định kiểu, kích thước, và loại font được hiển thị trên form và trong những controls trong form.
Size Kích thước của form bao gồm: Width và Height
Start Position
Thuộc tính xác định vị trị mặc định xuất hiện của Form trên màn hình máy tính người sử dụng, có các thuộc tính sau:
- Manual - vị trí và kích thước của form phục thuộc vào vị trí
xuất hiện của nó
- CenterScreen - xuất hiện ở chính giữa màn hình
106 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
- WindowsDefaultLocation - form xuất hiện tại vị trí mặc
định của Windows theo kích thước của form.
- Windows DefaultBounds - form được hiển thị tại vị trí mặc
định của Windows và các chiều của chúng phụ thuộc vào hệ điều hành Windows.
- Center Parent - form được mở như một cửa sổ con của một
form khác và xuất hiện tại vị trí chính giữa so với form cha.
Xác định tiêu đề của form tại Title Bar Text
WindowState
Xác định trạng thái xuất hiện của form: normal, maximized, hay minimized.
Sự kiện trong Windows Form
Event (sự kiện) là gì? - Sự kiện là những phản ứng của đối tượng. Nói cách khác, sự kiện là những tín hiệu
phát ra khi người dùng thao tác trên đối tượng.
- Nhờ có event, người lập trình sẽ nhận được những tín hiệu và xử lý những tín hiệu đó
để phản hồi lại cho người dùng, tạo nên sự nhịp nhàng cho chương trình.
Bảng trình bày các sự kiện (Events)
Sự kiện Mô tả
Click Gọi đến khi control bị Click. Trong một vài control, event này cũng
xảy ra khi người dùng nhấn phím Enter.
DoubleClick Gọi đến khi control bị Double-Click. Trong một vài control, event này
không báo giờ được gọi. Ví dụ: control Button.
Gọi đến khi việc “ rag and rop” được hoàn tất DragDrop
Gọi đến khi đối tượng vừa được “ rag” đến biên của control DragEnter
DragLeave Gọi đến khi đối tượng vừa được “ rag” ra ngoài biên của control
Gọi đến khi đối tượng được “ rag” bên trong control DragOver
KeyDown
Gọi đến khi vừa bấm một phím bất kỳ từ 1 control đang focus. Sự kiện này luôn được gọi trước sự kiện KeyUp
KeyPress
Gọi đến khi vừa bấm một phím bất kỳ từ 1 control được focus. Sự kiện này được gọi sau sự kiện KeyUp
KeyUp
Gọi đến khi vừa bấm một phím bất kỳ rồi thả ra từ 1 control đang focus.
Sự kiện này luôn được gọi sau sự kiện KeyDown
107 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
GotFocus Gọi đến khi control được focus
LostFocus Gọi đến khi control bị mất focus
MouseDown Gọi đến khi con trỏ chuột nằm trên 1 control và nút chuột được nhắp
nhưng chưa thả ra
MouseMove Gọi đến khi con trỏ chuột đi qua 1 control
MouseUp Gọi đến khi con trỏ chuột nằm trên 1 control và nút chuột vừa được thả
Paint Gọi đến khi control được vẽ
Validated Gọi đến khi control focus, property CaucesValidation được đặt là true
và sau khi gọi việc kiểm tra bằng Validating
Validating Gọi đến khi control mất focus, property CaucesValidation được đặt là
true.
Hàm thao tác với Windows
Form Methods Mô tả Ví dụ
Show() Được sử dụng để xuất hiện Form1 frmObj = new
một form bằng cách set Form1();
frmObj.Show();
thuộc tính Visible của form ấy là True
Activate() Sử dụng để kích hoạt trạng frmObj.Activate();
thái sử dụng của Form và đưa trạng thái sử dụng về
Form ấy.
Close() ùng để đóng một Form frmObj.Close();
SetDesktopLocation() Hàm này dùng để định vị SetDesktopLocation(100,1
trí của Form trên màn hình 50)
4.1.2 Hộp văn bản – TextBox TextBox là điều khiển cho phép nhận giá trị từ người dùng trên một Form. Mặc định
giá trị lớn nhất mà TextBox nhận là 2048 ký tự.
Ví dụ Property Mô tả
Text txtUserName.Text=”pta30000”;
Xác định giá trị hiển thị bên trong TextBox
Multiline thị txtContent.Multiline = true;
Cho phép TextBox hiển nhiều dòng chữ.
PasswordChar Thuộc tính này cho phép lấy một txtPassword.PasswordChar=”*”;
ký tự làm đại diện cho tất cả các
ký tự khác được nhập vào từ
108 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
người dùng
4.1.3 Nút – Button
Button được sử dụng để thực hiện một tác vụ khi người sử dụng nhấn vào nó. private void button1_Click(object sender, EventArgs e)
{
this.button1.Text = this.Text;
}
4.1.4 Nhãn – Label
Control được sử dụng để hiển thị chữ trên form và không cho phép người dùng
thay đổi. Label được sử dụng để mô tả thông tin cho những control khác trên Form.
òng chữ xuất hiện bên trên TextBox đó là Label, có mục đích giải thích cho
TextBox.
LinkLabel Control
LinkLabel được sử dụng để hiển thị một chuỗi như một liên kết. Khi bạn nhấn
vào liên kết, nó sẽ mở ra một form khác hoặc một Website.
Để mở ra một form khác, .NET cung cấp sự kiện LinkClicked, bạn thực hiện như sau:
linkLabel1_LinkClicked (object sender,
private void LinkLabelLinkClickedEventArgs e)
{
Form1 frmobj = new Form1();
frmobj.Show();
}
4.1.5 ToolTip
Tooltip là 1 đoạn mô tả ngắn gọn hiện lên khi di chuột qua 1 đối tượng điều
khiển. Tooltip tự động hiện ra khi di chuột, ấn vào đối tượng điều khiển và tự biến mất
khi di chuột ra ngoài. Mục đích của tooltip là nói cho người dùng biết đối tượng này
dùng để làm gì. Ta hoàn toàn có thể dùng Text hay các Image để hiện lên tooltip.
Style="{StaticResource BackButtonStyle}"
Cách dùng: Chọn một control cần thêm tooltip, sau đó chọn 1 trong các cách sau: - Thêm Text cho tooltip:
- Thêm Image cho tooltip:
109 Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện
Nếu muốn dùng Tooltip ở code behind, bạn cũng có thể làm theo các cách sau: - Thêm Text cho tooltip: 1. Tạo mới 1 đối tượng Tooltip, 2. Đặt thuộc tính Content cho Tooltip là 1 String. 3. ùng phương thức ToolTipService.SetToolTip để điều khiển Control muốn đặt
Tooltip
Toggled="onOffSwitch_Toggled"/> Codebehind: ToolTip toolTip = new ToolTip();
toolTip.Content = "Flip switch to turn on."; ToolTipService.SetToolTip(onOffSwitch, toolTip); ToggleSwitch được định nghĩa trong phần XAML và được đặt tên là onOffSwitch để thao tác trong codebehind. 4.2 Một số điều khiển khác 4.2.1 Hộp đánh dấu – CheckBox a. Công dụng Control này được dùng để gán tùy chọn Yes/No hoặc True/False. b. Thuộc tính Những thuộc tính thường sử dụng của CheckBox Thuộc tính Mô tả Ví du Text CheckBoxl.Text = "Yes"; Thuộc tính này sử dụng để nhận
hoặc gán chuỗi ký tự là tiêu đề của Checked Checkbox
Là thuộc tính được sử dụng để xác checkBox1.Checked = true; định checkbox được chọn 4.2.2 Nút tuỳ chọn – RadioButton
a. Công dụng RadioButton được dùng để cung cấp sự lựa chọn một trong một nhóm tiêu chí cho người dùng. Chỉ một RadioButton được chọn trong một nhóm. b. Thuộc tính Những thuộc tính thường sử dụng của RadioButton giống như CheckBox, bao gồm: Text, Checked. 4.2.3 Nhóm – GroupBox Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Được sử dụng để nhóm những control liên quan lại với nhau. GroupBox thường được sử dụng để nhóm hai hay nhiều RadioButton để cung cấp một sự lựa chọn duy nhất giữa chúng. 4.2.4 Hộp danh sách – ListBox a. Công dụng ListBox được sử dụng để hiển thị một danh sách phần tử đến người dùng. Một người dùng có thể chọn một trong những phần tử này. Bạn có thể thêm danh mục những phần tử vào trong một ListBox bằng cách,
chọn ListBox, tại cửa sổ Properties Window, chọn thuộc tính Items, nhấn vào nút và cửa sổ String Collection Editor xuất hiện như hình dưới. Nhập vào VietNam, gõ Enter, nhập vào USA, gõ Enter. Sau đó nhấn OK. ListBox của bạn sẽ có hai phần tử VietNam và USA. Bạn cũng có thể thêm những phần tử vào ListBox tại lúc chạy chương trình bằng cách sử dụng phương thức Add() của thuộc tính Items: ListBox. lstitems.Items.Add("German"); lstitems.Items.Add("China"); b. Thuộc tính
Những thuộc tính thường sử dụng nhất của ListBox được liệt kê theo bảng dưới đây: Thuộc tính Mô tả Ví dụ SelectionMode Xác định cách thức mà người dùng lựa ListBox1.SelectionMode = chọn những phần tử trong ListBox. Có SelectionMode. 4 giá trị MultiSimple; - None: người dùng không thể chọn bất
cứ phần tử nào trong ListBox
- One: người sử dụng chỉ chọn một giá
trị từ ListBox
- MultiSimple: cho phép người sử dụng chọn nhiều phần tử từ ListBox.
- MultiExtended: người dùng có thể
chọn nhiều phần tử và sử dụng phím
SHIFT, CTRL và phím mũi tên để chọn những phần tử từ ListBox. Sorted Thuộc tính xác định khi nào những ListBox1.Sorted = true; Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện thuộc tính trong ListBox được sắp xếp SelectedIndex Thuộc tính cho phép gán hoặc nhận vị ListBox1.SelectedIndex = trí được chọn hiện tại trong ListBox. 2; SelectedItem Thuộc tính này được dùng để gán hoặc MessageBox.Show( nhận phần tử đang được chọn ListBox1.SelectedItem) 4.2.5 Hộp lựa chọn – ComboBox a. Công dụng ComboBox được sử dụng để hiển thị danh sách những phần tử thả xuống. ComboBox là sự kết hợp của TextBox cho phép người dùng nhập giá trị và một danh sách thả xuống cho phép người sử dụng chọn phần tử. b. Thuộc tính Hầu hết những thuộc tính của ComboBox giống như thuộc tính của ListBox, nhưng ComboBox có thể thuộc tính Text. Thuộc tính Text: cho phép gán hoặc nhận giá trị được người sử dụng nhập vào từ ComboBox. Để thêm những phần tử vào ComboBox, tương tự như với ListBox, bằng cách sử dụng phương thức Add() của thuộc tính Items. ComboBox1.Items.Add(“VietNam”); ComboBox1.Items.Add(“Thailand”); 4.2.6 Điều khiển Panel a. Công dụng
- Bảng chứa các control hay một nhóm các control. b. Tạo Panel
- Chọn công cụ - Rê chuột và vẽ Panel. c. Thuộc tính Mô tả Thuộc tính AutoScroll Tự động cuộn nếu số control nằm ngoài
vùng BorderStyle None / FixedSingle /
Fixed3D 4.2.7 Điều khiển PictureBox a. Công dụng
- Khung chứa hình ảnh. b. Tạo PictureBox
- Chọn công cụ
- Rê chuột và vẽ PictureBox. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Thuộc tính Mô tả Image Hình chứa trong PictureBox 4.2.8 Điều khiển Timer a. Công dụng
- Quy định khoảng thời gian định kỳ để thực hiện một công việc. b. Tạo Timer
- Chọn công cụ - Rê chuột và vẽ Timer à là control dạng unvisible (ẩn). c. Thuộc tính Thuộc tính Mô tả Enabled Bật / tắt chế độ hẹn thời gian Interval Khoảng thời gian định kỳ 4.2.9 ErrorProvider ( ) a. Công dụng
- Hỗ trợ thông báo lỗi cho các control khác. - Thường được dùng với control input (ví dụ: TextBox) ràng buộc với 1 điều kiện nhập nào đó. b. Tạo ErrorProvider
- Chọn công cụ - Rê chuột và vẽ ErrorProvider à là control dạng unvisible (ẩn). 4.3 Các hộp thoại thông dụng 4.3.1 Hộp hội thoại Open File Open File là lớp công cụ cho phép người dùng kiểm tra sự tồn tại của một tập tin và mở nó. Hầu hết các chức năng của hộp thoại được tìm thấy trong lớp FileDialog. Hộp thoại này chỉ cho phép mở một tập tin do có thuộc tính ShowReadOnly, nếu bạn muốn mở một thư mục thì sử dụng hộp thoại FolderBrowser ialog để thay thế. Một số thuộc tính của hộp thoại Open File được liệt kê trong bảng sau: Thuộc tính Mô tả CheckFileExists Hộp thoại cảnh báo sẽ được hiển thị nếu người dùng yêu
cầu tập tin không tồn tại. CheckPathExxist Hộp thoại cảnh báo sẽ được hiển thị nếu người dùng yêu
cầu đường dẫn không tồn tại. FileName Trả về một chuỗi ký tự chứa tên tập tin được lựa chọn
trong hộp thoại FileNames Trả về tên của tất cả các tập tin được chọn trong hộp thoại Filter Thiết lập một chuỗi hiển thị trong hộp thoại “Save as file ” hoặc “Files of type” cho biết thông tin để lọc tên các tập Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện tin được lựa chọn FilterIndex Trả về chỉ số của bộ lọc của tập tin đang được lựa chọn Một số phương thức: Phương thức Mô tả OpenFile Mở tập tin với quyền đọc/ghi được lựa chọn bởi người dùng Reset Thiết lập lại các giá trị mặc định cho hộp thoại ShowDialog() Hiển thị một hộp thoại thông thường với người dùng mặc định Ví dụ: Đoạn code sau tạo ra một hộp thoại OpenFile ialog, thiết lập một số thuộc tính
và hiển thị hộp thoại bằng cách click vào nút button. private void button1_Click(object sender, System.EventArgs e) {
Stream myStream = null;
OpenFileDialog openFileDialog1 = new OpenFileDialog();
openFileDialog1.InitialDirectory = "c:\\" ;
openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ;
openFileDialog1.FilterIndex = 2 ;
openFileDialog1.RestoreDirectory = true ;
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
try
{
if ((myStream = openFileDialog1.OpenFile()) != null)
{
using (myStream)
{
// Insert code to read the stream here.
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original
error: " + ex.Message);
}
}
} Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 4.3.2 Hộp thoại SaveFile và luồng FileStream Save File là lớp công cụ cho phép người dùng mở và ghi đè lên một tập tin hiện có hoặc tạo ra một tập tin mới. Hầu hết các chức năng của hộp thoại được tìm thấy Một số thuộc tính của hộp thoại Open File được liệt kê trong bảng sau: trong lớp File ialog. Thuộc tính Mô tả CheckFileExists Hộp thoại cảnh báo sẽ được hiển thị nếu người dùng chỉ
định tập tin không tồn tại. CheckPathExxist Hộp thoại cảnh báo sẽ được hiển thị nếu người dùng yêu
cầu đường dẫn không tồn tại. FileName Trả về một chuỗi ký tự chứa tên tập tin được lựa chọn trong hộp thoại FileNames Trả về tên của tất cả các tập tin được chọn trong hộp thoại Filter Thiết lập một chuỗi hiển thị trong hộp thoại “Save as file ” hoặc “Files of type” cho biết thông tin để lọc tên các tập tin được lựa chọn FilterIndex Trả về chỉ số của bộ lọc của tập tin đang được lựa chọn OverWritePrompt Hiển thị hộp thoại “Save as” nếu người dùng chỉ định một tập tin đã tồn tại CreatePrompt Hiển thị hộp thoại nhắc nhở nếu người dùng chỉ định tập tin không tồn tại Các phương thức giống như hộp thoại OpenFileDialog. Ví dụ: Đoạn code sau tạo ra một hộp thoại SaveFile ialog, thiết lập một số thuộc tính và hiển thị hộp thoại bằng cách click vào nút button. private void button1_Click(object sender, System.EventArgs e)
{
Stream myStream ;
SaveFileDialog saveFileDialog1 = new SaveFileDialog(); saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ;
saveFileDialog1.FilterIndex = 2 ;
saveFileDialog1.RestoreDirectory = true ; if(saveFileDialog1.ShowDialog() == DialogResult.OK)
{ if((myStream = saveFileDialog1.OpenFile()) != null)
{
// Code to write the stream goes here.
myStream.Close(); Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện } }
} 4.3.3 Hộp thoại Color Color ialog là lớp công cụ màu sắc cho phép người sử dụng lựa chọn màu sắc từ bảng màu. Sử dụng công cụ này bằng cách kéo thả ColorDialog từ ToolBox vào trong Form. Bạn mong muốn rằng sau khi nhấn Button “Change Backgroud Color” và
lựa chọn màu sắc thì màu nền của Form sẽ thay đổi theo màu được chọn, bạn thực
hiện như sau: Tại sự kiện Click() của Button, thực hiện đoạn mã lệnh private void button1_Click(object sender, EventArgs e)
{
colorDialog1.ShowDialog();
this.BackColor = colorDialog1.Color;
} Màu nền của Form đã được thay đổi khi lựa chọn màu từ bảng màu: Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 4.3.4 Hộp thoại Font Giống như Color ialog, Font ialog được sử dụng để người dùng lựa chọn kiểu hiển thị, kích thước của chữ. Để sử dụng Font ialog, bạn thực hiện theo các bước sau đây - Kéo thả Font ialog từ ToolBox vào Form. - Thực hiện đoạn mã lệnh sau private void button2_Click(object sender, EventArgs e){ fontDialog1.ShowDialog(); this.label1.Font = fontDialog1.Font; } Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Lựa chọn Font để thay đổi với Font ialog 4.4 Menu 4.4.1 Menu – MenuStrip Trong môi trường Windows, bạn có thể sử dụng Menu để tăng sự tiện ích cho người sử dụng ứng dụng. Menus có hai dạng, Menu xuất hiện tại thanh Menu và menu xuất hiện khi người sử dụng nhấn chuột phải, được gọi là Context menus. Menus xuất hiện tại Menu Bar được khởi tạo bằng cách kéo thả control MenuStrip từ ToolBox vào Form esign, khi ấy Menu xuất hiện cho phép bạn khởi tạo danh mục menu như hình sau Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 4.4.2 Popup menu – ContextMenuStrip Context Menus được xuất hiện khi người sử dụng nhấn chuột phải. Để tạo ra context menu, bạn có thể thực hiện như sau: Kéo thả điều khiển ContextMenuStrip vào cửa sổ thiết kế ứng dụng. ContextMenu xuất hiện như hình sau: Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Chương 5: Lập trình cơ sở dữ liệu 5.1 Hệ quản trị cơ sở dữ liệu MS SQL Server 2008 Trong một thế giới dữ liệu ngày nay, dữ liệu và các hệ thống quản lý dữ liệu đó cần phải luôn luôn được bảo đảm và ở trạng thái có sẵn. SQL Server 2008 cho phép
các nhà phát triển giảm được sự phức tạp của cơ sở hạ tầng trong khi đó vẫn bảo đảm cung cấp một nền tảng dữ liệu doanh nghiệp có khả năng bảo mật, khả năng mở rộng và quản lý tốt hơn, cùng với thời gian chết của ứng dụng giảm. 5.1.1 Tổng quan về SQL Server 2008 Nền tảng cho các nhiệm vụ then chốt - SQL Server 2008 cho phép các tổ chức có thể chạy hầu hết các ứng dụng phức tạp của họ trên một nền tảng an toàn, tin cậy và có khả năng mở rộng. Bên cạnh đó còn giảm được sự phức tạp trong việc quản lý cơ sở hạ tầng dữ liệu. SQL Server 2008 cung cấp một nền tảng tin cậy và an toàn bằng
cách bảo đảm những thông tin có giá trị trong các ứng dụng đang tồn tại và nâng cao khả năng sẵn có của dữ liệu. SQL Server 2008 giới thiệu một cơ chế quản lý cách tân dựa trên chính sách, cơ chế này cho phép các chính sách có thể được định nghĩa quản trị tự động cho các thực thể máy chủ trên một hoặc nhiều máy chủ. Thêm vào đó, SQL Server 2008 cho phép thi hành truy vấn dự báo với một nền tảng tối ưu. Sự phát triển động - SQL Server 2008 cùng với .NET Framework đã giảm được sự phức tạp trong việc phát triển các ứng dụng mới. A O.NET Entity Framework cho
phép các chuyên gia phát triển phần mềm có thể nâng cao năng suất bằng làm việc với các thực thể dữ liệu logic đáp ứng được các yêu cầu của doanh nghiệp thay vì lập trình trực tiếp với các bảng và cột. Các mở rộng của ngôn ngữ truy vấn tích hợp (LINQ) mới trong .NET Framework đã cách mạng hóa cách các chuyên gia phát triển truy vấn dữ liệu bằng việc mở rộng Visual C#® và Visual Basic® .NET để hỗ trợ cú pháp truy vấn giống SQL vốn đã có. Hỗ trợ cho các hệ thống kết nối cho phép chuyên gia phát triển xây dựng các ứng dụng cho phép người dùng mang dữ liệu cùng với ứng dụng này vào các thiết bị và sau đó đồng bộ dữ liệu của chúng với máy chủ trung tâm. - Dữ liệu quan hệ mở rộng - SQL Server 2008 cho phép các chuyên gia phát
triển khai thác triệt để và quản lý bất kỳ kiểu dữ liệu nào từ các kiểu dữ liệu truyền
thống đến dữ liệu không gian địa lý mới. - Thông tin trong toàn bộ doanh nghiệp - SQL Server 2008 cung cấp một cơ sở
hạ tầng có thể mở rộng, cho phép quản lý các báo cáo, phân tích với bất kỳ kích thước
và sự phức tạp nào, bên cạnh đó nó cho phép người dùng dễ dàng hơn trong việc truy
cập thông tin thông qua sự tích hợp sâu hơn với Microsoft Office. Điều này cho phép CNTT đưa được thông tin của doanh nghiệp rộng khắp trong tổ chức. SQL Server 2008 tạo những bước đi tuyệt vời trong việc lưu trữ dữ liệu, cho phép người dùng hợp
nhất các trung tâm dữ liệu vào một nơi lưu trữ dữ liệu tập trung của toàn doanh nghiệp. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 5.1.2 SQL Server Management Studio Mở SQL Server Management Studio ta làm như sau: Vào start -> chọn program -> chọn Microsoft SQL Server 2008 -> chọn SQL Server Management Studio. Chú ý những thành phần trên hộp thoại sau:
- Server Type: Như ví dụ của cuốn sách này, cho phép server type là atabase Engine. Các tùy chọn khác là kiểu dữ liệu khác nhau của servers nó sẽ hiển thị kết nối. - Server Name: Hộp combo thứ 2 chứa 1 danh sách của SQL Server cài đặt mà chọn. Trong hộp thoại hình 12, bạn sẽ thầy tên của máy tính được cài đặt trên local. Nếu bạn mở hộp Server name bạn có thể tìm kiếm nhiều server local hoặc network connection bằng cách chọn - Authentication: Combobox cuối cùng xác định các loại hình kết nối bạn muốn sử dụng.Trong giáo trình này chúng ta kết nối đến SQL Server sử dụng Windowns
Authentication. Nếu bạn cài đặt SQL Server với chế độ hỗn hợp(mix mode), thì bạn có
thể thay đổi chọn lựa SQL Server authentication, thì nó sẽ mở hai hộp thoại và cho
phép nhập username và password.
Sau khi nhấn nút Connect sẽ xuất hiện màn hình sau: Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 5.1.3 Tạo cơ sở dữ liệu (database)
Chọn database -> Click phải -> Chọn New atabase… Trong hộp thoại New atabase đặt tên cho database name -> Chọn OK Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Tạo bảng (table) Vào database quản lý bán hàng chọn table. Sau đó click phải lên table -> Chọn New Table. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Khi chọn New Table sẽ xuất hiện bên phải màn hình bên dưới Sau đó ta nhập Column Name, ata Type… Nhấn Enter để nhập cột kết tiếp. Lưu table trên thanh Standard toolbar -> chọn Save. Tạo quan hệ kết nối giữa các bảng (relatetionship) gồm 2 bước: Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Bước 1. Tạo khóa chính (Primary key) cho table trong SQL Server Management Studio, tạo cột và kiểu dữ liệu. Sau đó trên thanh toolbar, chọn nút Set Primary Key . Bạn cũng có thể click phải lên column chọn Set Primary Key. Bước 2: Tạo khóa ngoại (Foreign key) trong cửa sổ thiết kế table. Foreign được dùng để liên kết các table lại với nhau. Cần lưu ý khi tạo foreign key là tên cột, kiểu dữ liệu giống tên cột của khóa chính mà table nó đại diện. Ví dụ sau Ma MSP làm foreign key Bước 3: Tạo sơ đồ (Diagrams) iagrams là 1 cửa sổ hiển thị mối quan hệ giữa các table của 1 database. Tạo diagram ta thực hiện như sau: - Trong cửa sổ Object Explorer chọn tên database cần tạo -> Click phải vào Database Diagrams -> Chọn New atabase iagram Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện - Sau khi chọn New atabase iagram sẽ xuất hiện hộp thoại để Add các table, sau khi add xong chọn Close. Để thiết lập mối quan hệ giữa các table ta chọn cột dữ liệu của cột làm khóa
chính trong bảng cha (parent table) và kéo nó đến khóa ngoại trong bảng con (child
table) Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện - Sau khi kéo mối quan hệ cho 2 table sẽ xuất hiện hộp thoại như hình trên. - Khi ta chọn OK giữa 2 table sẽ xuất hiện một kết nối giữa 2 table 5.1.3 Truy vấn dữ liệu cơ bản
a. Khái niệm cơ bản về T-SQL
Transact-SQL là ngôn ngữ SQL mở rộng dựa trên SQL chuẩn của ISO (International Organization for Standardization) và ANSI (American National Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Standards Institute) được sử dụng trong SQL Server khác với PL-SQL ((Procedural Language/Structured Query Language) dùng trong Oracle. Data Definition Language (DDL):
Ðây là những lệnh dùng để quản lý các thuộc tính của một database như định nghĩa
các hàng hoặc cột của một table, hay vị trí data file của một database..., thường có dạng như sau : - Create object_Name
- Alter object_Name
- Drop object_Name
Trong đó object_Name có thể là một table, view, stored procedure, indexes... Ví dụ: Lệnh Create sau sẽ tạo ra một table tên Importers với 3 cột CompanyI , CompanyName, Contact USE Northwind (sử dụng cơ sở dữ liệu Northwind sẵn có của
SQL Server 2008) CREATE TABLE Importers (CompanyID int NOT NULL, CompanyName varchar(40) NOT NULL, Contact varchar(40) NOT NULL). Lệnh Alter sau đây cho phép ta thay đổi định nghĩa của một table như thêm(hay bớt) một cột hay một Constraint... Trong ví dụ này ta sẽ thêm cột ContactTitle vào table Importers USE Northwind ALTER TABLE Importers ADD ContactTitle varchar(20) NULL Lệnh rop sau đây sẽ hoàn toàn xóa table khỏi database nghĩa là cả định nghĩa của table và data bên trong table đều biến mất (khác với lệnh elete chỉ xóa data nhưng
table vẫn tồn tại). USE Northwind ROP TABLE Importers Data Control Language (DCL):
Ðây là những lệnh quản lý các quyền truy cập lên từng object (table, view, stored procedure...). Thường có dạng sau: - Grant
- Revoke
- Deny Ví dụ: Lệnh sau sẽ cho phép user trong Public Role được quyền Select đối với table
Customer trong database Northwind (Role là một khái niệm giống như Windows
Group sẽ được bàn kỹ trong phần Security) USE Northwind GRANT SELECT ON
Customers TO PUBLIC Lệnh sau sẽ từ chối quyền Select đối với table Customer trong
database Northwind của các user trong Public Role USE Northwind ENY SELECT
ON Customers TO PUBLIC Lệnh sau sẽ xóa bỏ tác dụng của các quyền được cho
phép hay từ chối trước đó USE Northwind REVOKE SELECT ON Customers TO PUBLIC Data Manipulation Language (DML): Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Ðây là những lệnh phổ biến dùng để xử lý data như Select, Update, Insert, elete Ví dụ:
Select USE Northwind SELECT CustomerID, CompanyName, ContactName FROM Customers WHERE (CustomerID = 'alfki' OR CustomerID = 'anatr')
ORDER BY ContactName Insert USE Northwind INSERT INTO Territories VALUES (98101, 'Seattle', 2) Insert USE Northwind INSERT INTO Territories VALUES (98101, 'Seattle', 2) Update USE Northwind UPDATE Territories SET TerritoryDescription =
'Downtown Seattle' WHERE TerritoryID = 98101 Delete USE Northwind DELETE FROM Territories WHERE TerritoryID = 98101 Chú ý : trong lệnh elete bạn có thể có chữ From hay không đều được. Để kiểm tra các ví dụ trên ta làm như sau: Trên thanh toolbar của màn hình SQL Server
Mangement Studio -> Chọn New Query và gõ các câu lệnh như trên. Sau đây là 1 ví dụ tạo table bằng câu lệnh T-SQL. Cú pháp của T-SQL Phần này chúng ta sẽ bàn về các thành phần tạo nên cú pháp của T-SQL
Identifiers Ðây chính là tên của các database object. Nó dùng để xác định một object.
(Chú ý khi nói đến Object trong SQL Server là chúng ta muốn đề cập đến table, view, stored procedure, index.....Vì hầu như mọi thứ trong SQL Server đều được thiết kế
theo kiểu hướng đối tượng (object-oriented)). Trong ví dụ sau TableName, KeyName, escription là những identifiers CREATE TABLE TableName (KeyName INT PRIMARY KEY, escription NVARCHAR(80)) Có hai loại Identifiers một loại Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện thông thường (Regular Identifier) và một loại gọi là Delimited Identifier, loại này cần có dấu "" hay dấu [] để ngăn cách. Loại elimited được dùng đối với các chữ trùng với từ khóa của SQL Server (reserved keyword) hay các chữ có khoảng trống. Ví dụ: SELECT * FROM [My Table] WHERE [Order] = 10 Trong ví dụ trên chữ Order
trùng với keyword Order nên cần đặt trong dấu ngoặc vuông []. Hàm (Functions) Có 2 loại hàm một loại là built-in và một loại user-defined Các hàm Built-In được chia làm 3 nhóm: - Rowset Functions : Loại này thường trả về một object và được đối xử như một table.
Ví dụ như hàm OPENQUERY sẽ trả về một recordset và có thể đứng vị trí của một table trong câu lệnh Select. - Aggregate Functions : Loại này làm việc trên một số giá trị và trả về một giá trị đơn hay là các giá trị tổng. Ví dụ như hàm AVG sẽ trả về giá trị trung bình của một cột.
- Scalar Functions : Loại này làm việc trên một giá trị đơn và trả về một giá trị đơn. Trong loại này lại chia làm nhiều loại nhỏ như các hàm về toán học, về thời gian, xử lý kiểu dữ liệu String....Ví dụ như hàm MONTH('2002-09-30') sẽ trả về tháng 9. - Các hàm User-Defined (được tạo ra bởi câu lệnh CREATE FUNCTION và phần body thường được gói trong cặp lệnh BEGIN...EN ) cũng được chia làm các nhóm như sau: - Scalar Functions : Loại này cũng trả về một giá trị đơn bằng câu lệnh RETURNS.
- Table Functions : Loại này trả về một table - Chú Thích (Comments) T-SQL dùng dấu -- để đánh dấu phần chú thích cho câu lệnh đơn và dùng /*...*/ để chú thích cho một nhóm b. Thực thi các câu lệnh SQL
- Thực thi một câu lệnh đơn: Một câu lệnh SQL được phân ra thành các thành phần cú pháp như trên bởi một parser, sau đó SQL Optimizer (một bộ phận quan trọng của SQL Server) sẽ phân tích và tìm cách thực thi (Execute Plan) tối ưu nhất ví dụ như cách nào nhanh và tốn ít tài nguyên của máy nhất... và sau đó SQL Server Engine sẽ thực thi và trả về kết quả.
- Thực Thi một nhóm lệnh (Batches) Khi thực thi một nhóm lệnh SQL Server sẽ
phân tích và tìm biện pháp tối ưu cho các câu lệnh như một câu lệnh đơn và chứa
execution plan đã được biên dịch (compiled) trong bộ nhớ sau đó nếu nhóm lệnh
trên được gọi lại lần nữa thì SQL Server không cần biên dịch mà có thể thực thi
ngay điều này giúp cho một batch chạy nhanh hơn. Lệnh GO Lệnh này chỉ dùng để
gởi một tín hiệu cho SQL Server biết đã kết thúc một batch job và yêu cầu thực thi. Nó vốn không phải là một lệnh trong T-SQL. 5.2 Kết nối CSDL 5.2.1 Giới thiệu về ADO.NET Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Ngày nay, những ứng dụng thương mại cần thiết để quản lý khối lượng dữ liệu khổng lồ. ữ liệu thường được lưu trữ trong những bảng quan hệ trong cơ sở dữ liệu. Nhận dữ liệu và thao tác dữ liệu trực tiếp từ một cơ sở dữ liệu đòi hỏi kiến thức về những câu lệnh thao tác trên cơ sở dữ liệu để truy cập đến dữ liệu. Và ứng dụng cần
giao tiếp với Cơ sở dữ liệu để thực hiện những công việc sau: - Nhận dữ liệu được lưu trữ trong cơ sở dữ liệu và hiển thị chúng ra giao diện người dùng. - Cập nhật dữ liệu, thực hiện thêm, hiệu chỉnh, và xóa dữ liệu. A O.NET là một mô hình được những ứng dụng .NET sử dụng để giao tiếp với cơ sở dữ liệu cho việc nhận, truy cập, và cập nhật dữ liệu. Mô hình ADO.NET Một data provider được sử dụng cho việc kết nối đến cơ sở dữ liệu, nhận, lưu trữ dữ liệu trong dataset, đọc, nhận và cập nhật dữ liệu trong cơ sở dữ liệu.
Có hai loại data provider: - Ole DB data provider - loại này làm việc với tất cả Ole B Provider như Sql
Ole B Provider, Oracle Ole b provider, và Jet Ole b Provider. Được biết đến trong môi trường .NET với namespace System. ata.Ole b. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện - Sql Server data provider - loại này làm việc chỉ với Microsoft SQL Server. Cho phép thao tác với tốc độ tối ưu với hệ cơ sở dữ liệu MSSQL Server, được biết đến với namespace System. ata.SqlClient. 5.2.2 Kết nối Kết nối là một component được sử dụng để thiết lập một kết nối đến cơ sở dữ liệu từ một data source. Có hai kiểu đối tượng kết nối thường sử dụng nhất là Ole bConnection và SqlConnection. Bảng sau đây hiển thị những thuộc tính và phương thức hay sử dụng nhất của một đối tượng kết nối. Tên Mô tả ConnectionString Cung cấp thông tin như datasource, tên cơ sở dữ liệu, được sử dụng để thiết lập kết nối với một cơ sở dữ liệu Open() Mở một kết nói với datasource được khai báo tại ConnectionString Close() Được sử dụng để đóng kết nối với data source State Được sử dụng để kiểm tra trạng thái của một kết nối. 0: kết nối đang đóng, 1: kết nối đang mở. Data Adapter ata Adapter là thành phần của A O.NET, có tác dụng chuyển tiếp dữ liệu từ và đến cơ sở dữ liệu. data Adapter nhận dữ liệu từ cở sở dữ liệu vào một ataSet. Khi bạn thay đổi dữ liệu trong dataset, cũng là thay đổi trong cơ sở dữ liệu bởi dataadapter.
Có hai kiểu data adapter thường dùng để cấu hình kết nối đến cơ sở dữ liệu trong Visual Studio .NET: - SqlDataAdapter - làm việc chỉ với hệ cơ sở dữ liệu MS SQL Server - OleDbDataAdapter - kiểu này được cấu hình để làm việc với hầu hết các hệ cơ sở dữ liệu được hỗ trợ bởi Ole b data provider. ata Adapter sử dụng đối tượng kết nối Ole bConnection và SqlConnection được cung cấp bởi data provider để giao tiếp với cơ sở dữ liệu. Thuộc tính và phương thức của Data Adapter ata Adapter giao tiếp với cơ sở dữ liệu trong khi nhận, thêm mới, xóa và cập
nhật dữ liệu. Những thuộc tính sau đây được thiết lập để thực hiện những tác vụ khác
nhau trên một hệ cơ sở dữ liệu
- SelectCommand - nhận dữ liệu từ CS L thông qua một câu truy vấn hoặc stored
procedures
- InsertCommand - thêm dữ liệu vào CS L qua câu lệnh insert.
- UpdateCommand - cập nhật cơ sở dữ liệu với câu lệnh update - DeleteCommand - xóa dữ liệu khỏi cơ sở dữ liệu thông qua câu lệnh delete - Fill() - là phương thức đẩy dữ liệu từ CS L vào một dataset. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện - Update() - là phương thức thực thi InsertComand, Update Command, hoặc eleteCommand cho mỗi câu lệnh thêm, hiệu chỉnh hoặc xóa dòng để thực hiện thay đổi trong CS L. Data Command ataCommand là đối tượng thực thi những câu lệnh SQL hoặc stored procedure được sử dụng để thao tác với CS L. ata Command là đối tượng của lớp SqlCommand và OleDbCommand. Ví dụ sử dụng ata Command: string connectionstring = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET"; OleDbConnection conObj = new OleDbConnection(connectionstring); conObj.Open();
OleDbCommand cmd = new OleDbCommand("delete from books where ibookid='003'",conObj); int result = cmd.ExecuteNonQuery(); conObj.Close(); string connectionstring = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET"; OleDbConnection conObj = new OleDbConnection(connectionstring);
conObj.Open(); OleDbCommand cmd = new OleDbCommand("select * from books"); OleDbDataReader dr; dr = cmd.ExecuteReader(); while (dr.Read()) { lbltestdatareader.Text = dr["BookID"].ToString(); } conObj.Close();
} 5.3 Các đối tượng hiển thị và xử lý dữ liệu
5.3.1 DataSet Là đối tượng nhận dữ liệu từ cơ sở dữ liệu thông qua ataAdapter, ataSet hoạt động như một cơ sở dữ liệu ảo chứa những bảng, dòng, và cột. Mô hình ataSet như sau Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Các đối tượng thuộc tính cần quan tâm với đối tượng ataSet bao gồm: DatatableCollection, DataRelationCollection, ExtendedProperties, DataTable, DataRelation, DataRowCollection, DataView, PrimaryKey, DataColumnCollection. Chi tiết những đối tượng thuộc tính này sẽ được đề cập trong các phần sau. Ví dụ sử dụng ataAdapter đẩy dữ liệu từ cơ sở dữ liệu vào ataSet. private void button3_Click(object sender, EventArgs e)
{ string connectionstring = "PROVIDER=SQLOLEDB;server=(local);uid=_net;pwd=;database=_NET"; OleDbConnection conObj = new OleDbConnection(connectionstring); conObj.Open(); OleDbCommand cmd = new OleDbCommand("select * from books"); OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "books");
conObj.Close();
} a. Lọc và sắp xếp dữ liệu ữ liệu lấy về từ cơ sở dữ liệu bởi câu lệnh truy vấn select bao gồm tất cả các
dòng dữ liệu trong một bảng. Yêu cầu đặt ra là chỉ lấy một số dòng xác định tương ứng với một mã số nào đó. Để giải quyết vấn đề này, A O.NET cung cấp hai cách thức để thực hiện: lọc dữ liệu trong ataSet và thực hiện lấy dữ liệu cần thiết qua câu lệnh
SQL có tham số. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện b. Lọc một Dataset ữ liệu từ một table trong cơ sở dữ liệu được lất toàn bộ và đổ vào ataset, sau đó tiến hành lọc những dòng dữ liệu cần thiết. Ví dụ sau mô tả cách thực hiện này. private void button4_Click(object sender, EventArgs e) { string connectionstring = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET";
OleDbConnection conObj = new OleDbConnection(connectionstring); conObj.Open(); OleDbCommand cmd = new OleDbCommand("select * from books",conObj); OleDbDataAdapter da = new OleDbDataAdapter(cmd);
DataSet ds = new DataSet(); da.Fill(ds, "books"); MessageBox.Show( ds.Tables["books"].Rows.Count.ToString()); // filter data DataView dv = new DataView(ds.Tables["books"]); dv.RowFilter = "fprice>20000"; dv.Sort = "fprice DESC";
MessageBox.Show(dv.Table.Rows.Count.ToString()); conObj.Close(); } c. Thêm, cập nhật, xóa dữ liệu trong cơ sở dữ liệu Thêm mới dữ liệu vào cơ sở dữ liệu string connectionstring = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET"; OleDbConnection conObj = new OleDbConnection(connectionstring); conObj.Open();
OleDbCommand cmd = new OleDbCommand("insert books values(?,?,?,?)",
conObj);
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue(“@inxb", 2);// Nha xuat ban tre
cmd.Parameters.AddWithValue(“@vmasach", "MS0002");
cmd.Parameters.AddWithValue(“@vtensach", "Lập trình .NET cho cuộc sống"); cmd.Parameters.AddWithValue(“@fgia", 75000);
cmd.ExecuteNonQuery(); Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện MessageBox.Show("Đã thêm một sách vào CSDL"); conObj.Close(); private void button7_Click(object sender, EventArgs e) {
string connectionstring = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET"; OleDbConnection conObj = new OleDbConnection(connectionstring); conObj.Open();
OleDbCommand cmd = new OleDbCommand("update books set vtensach=?,fgia=? where vmasach=?",conObj); cmd.CommandType = CommandType.Text; cmd.Parameters.AddWithValue(“@vtensach", ".NET for Real Life");
cmd.Parameters.AddWithValue(“@fgia", 85000); cmd.Parameters.AddWithValue(“@vmasach", "MS0002"); cmd.ExecuteNonQuery(); MessageBox.Show("Đã sửa đổi nội dung sách trong CSDL"); conObj.Close(); } 5.3.2 Xây dựng một lớp CSDL dùng chung Khi thao tác với CS L, việc tạo kết nối, mở kết nối, thực hiện câu truy vấn dữ liệu, … và đóng kết nối được thực hiện rất nhiêu lần, Sẽ là rất tốt thời gian nếu chúng ta cứ lập đi lập lại quá trình này. Chúng ta sẽ cùng xây dựng một lớp chuyên biệt để thực hiện việc giao tiếp với CS L, và người lập trình sẽ không phải mất công thực hiện lại các bước thao tác với các đối tượng như Connection, ataAdapter… và cuối cùng là giúp giảm thiểu thời gian cho việc xây dựng ứng dụng. Xây dựng lớp giao tiếp với CSDL – DBClass
Mã lệnh của lớp BClass như sau using System;
using System.Data;
using System.Data.OleDb;
public class OleDBClass
{ static string strConStr = ""; public static string ConnectingString { get Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện { if (!strConStr.Equals("")) return strConStr; else
return null; } set { strConStr = value; } } public static OleDbConnection GetConnection()
{ OleDbConnection con = new OleDbConnection(strConStr); con.Open(); return con; } public static DataTable GetData(OleDbCommand cmd) { try { if (cmd.Connection != null) { using (DataSet ds = new DataSet()) { using (OleDbDataAdapter da = new OleDbDataAdapter()) { da.SelectCommand = cmd;
da.Fill(ds);
return ds.Tables[0];
} } }
else { using (OleDbConnection conn = GetConnection())
{ Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện using (DataSet ds = new DataSet()) { using (OleDbDataAdapter da = new OleDbDataAdapter()) {
da.SelectCommand = cmd; da.SelectCommand.Connection = conn; da.Fill(ds); return ds.Tables[0];
} } } } } finally { } } public static OleDbDataReader GetReadOnlyData(OleDbCommand cmd) { try
{ if (cmd.Connection != null) { return cmd.ExecuteReader(); } else { using (OleDbConnection conn = GetConnection()) { cmd.Connection = conn;
datareader = cmd.ExecuteReader();
return cmd.ExecuteReader(); } } } finally {} } Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện public static DataSet GetDsData(OleDbCommand cmd) { try { if (cmd.Connection != null) { using (DataSet ds = new DataSet()) {
using (OleDbDataAdapter da = new OleDbDataAdapter()) { da.SelectCommand = cmd; da.Fill(ds);
return ds; } } } else { using (OleDbConnection conn = GetConnection())
{ using (DataSet ds = new DataSet()) { using (OleDbDataAdapter da = new OleDbDataAdapter()) { da.SelectCommand = cmd; da.SelectCommand.Connection = conn; da.Fill(ds); return ds; }
} } } }
finally { } } public static DataTable GetData(string sql) Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện { try { using (OleDbConnection conn = GetConnection())
{ using (OleDbCommand cmd = conn.CreateCommand()) { cmd.CommandType = CommandType.Text;
cmd.CommandText = sql; using (DataSet ds = new DataSet()); { using (OleDbDataAdapter da = new OleDbDataAdapter())
{ da.SelectCommand = cmd; da.SelectCommand.Connection = conn; da.Fill(ds); return ds.Tables[0]; } } } } } finally {} } public static void ExecuteNonQuery(OleDbCommand cmd) { try
{ using (OleDbConnection conn = GetConnection())
{ cmd.Connection = conn;
cmd.ExecuteNonQuery(); } } finally {} } Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện public static object ExecuteScalar(OleDbCommand cmd) { try { using (OleDbConnection conn = GetConnection()) { cmd.Connection = conn; return cmd.ExecuteScalar(); } } finally { } }
} Sử dụng lớp giao tiếp với CSDL - DBClass Việc bây giờ là chúng ta sẽ sử dụng lớp CS L như thế nào. Sau đây là mã lệnh mẫu để sử dụng. DBClass.ConnectingString = "PROVIDER=SQLOLEDB; server=(local);uid=_net;pwd=;database=_NET";
// việc gọi một câu truy vấn bây giờ đơn giản là việc gọi hàm trong lớp DBClass DataTable dt = DBClass.GetData("select * from items"); Chúng ta có thể thấy đó là việc thực hiện câu truy vấn dữ liệu từ bảng items đã trở nên cực kỳ đơn giản hơn bao giờ hết. Không cần phải mở kết nối Connection, sử dụng ataAdapter… bạn vẫn có được dữ liệu từ bảng items. Nói cho đúng thì chúng ta đã đưa những công doạn này vào trong lớp Ole BClass và giờ đây chúng ta chỉ quan tâm tới mục đích sử dụng câu truy vấn mà không mất quá nhiều mã lệnh để thực hiện các bước chuẩn bị. 5.4 Thiết kế báo cáo Crystal Reports for Visual Studio .NET là công cụ báo cáo chuẩn dành cho
Visual Studio.NET. Bạn có thể tiếp quản những báo cáo này trên nền Web và Windows,
và phân bổ chúng ở dạng dịch vụ Web trên một server. Mục này sẽ trình bày từng bước một cách sinh báo cáo từ một nguồn dữ liệu.
Trước tiên, bạn tạo một dự án ứng dụng Windows mới với tên là CrystalExample. Để có thể hiển thị Crystal Report, bạn cần thực hiện hai bước chính: thứ nhất là xây dựng một bản báo cáo, và thứ hai là thêm một điều kiểm CrystalReportViewer vào form để
trình bày báo cáo. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Việc xây dựng một bản báo cáo được mô tả trong 11 bước nhỏ dưới đây: 1. Từ thanh trình đơn chính của IDE, chọn Project | Add New Item và chọn Crystal Report. Nhắp Open. 2. Crystal Report Wizard sẽ nhắc ta chọn kiểu báo cáo cần xây dựng. Giữ nguyên báo cáo chuẩn mặc định, rồi nhắp OK. 3. Crystal Report Wizard hiển thị hộp thoại Standard Report Expert. Nhắp vào dấu
cộng kế thư mục OLE DB (ADO) trong hộp danh sách Available Data Sources. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 4. Bạn sẽ thấy hộp thoại OLE DB (ADO). Chọn Microsoft OLE DBProvider for SQL Server, rồi nhắp Next. 5. Kế tiếp, chúng ta cần báo với trình thuật sĩ cơ sở dữ liệu nào sẽ được kết nối. Chọn cơ sở dữ liệu Northwind. Nhắp Next. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 6. Crystal Report Wizard hiển thị hộp thoại Advanced Information. Chúng ta không cần thay đổi thông tin nào cho ví dụ này, cho nên nhắp Finish. Trong cửa sổ Standard Report Expert, chọn bảng Categories và Products cho bản báo cáo của chúng ta. Nhắp Next. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 7. Trong thẻ Links của hộp thoại Standard Report Expert, giữ nguyên các mặc định như hình. Bạn có thể thấy trình thuật sĩ ánh xạ khóa chính trong Categories đến khóa ngoại trong Products. Nhắp Next để hiển thị thẻ Fields. 8. Từ bảng Categories, chọn CategoryName và Description. Từ bảng Products, chọn ProductName, QuantityPerUnit, UnitPrice, và UnitsInStock (xem h.nh 10.23). Nhắp Next để hiển thị thẻ Group. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 9. Chọn CategoryName và ProductName để phân nhóm. 10. Nhắp Next nhiều lần để chấp nhận các thiết lập mặc định cho các thẻ Total, Top N, Chart, và Select. Khi đến thẻ cuối cùng (thẻ Style), g. tiêu đề Category - Product
Report (xem h.nh 10.25). Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện 11. Nhắp Finish. File CrystalReport1.rpt (xem h.nh 10.26) sẽ được thêm vào dự án. Chúng ta đã xây dựng xong bản báo cáo. Bước thứ hai là thiết lập một cơ chế để xem bản báo cáo này. Bước này khá dễ dàng. Bạn h.y kéo điều kiểm CrystalReportViewer từ hộp công cụ vào form mặc định. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện Nhắp phải vào CrystalReportViewer, và chọn Properties. Tìm thuộc tính Dock và chọn Fill Tùy chọn này sẽ khiến CrystalReportViewer lấp đầy vùng client của form. Kế tiếp, tìm thuộc tính ReportSource, và nhắp Browse. Chọn file CrystalReport1.rpt mà chúng ta vừa xây dựng. Bây giờ, bạn chạy chương trình để xem kết quả. Bài giảng Ngôn ngữ lập trình ứng dụng – Ngành Truyền thông đa phương tiện110
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
111
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
112
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
113
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
114
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
115
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
116
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
117
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
118
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
119
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
120
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
121
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
122
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
123
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
124
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
125
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
126
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
127
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
128
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
129
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
130
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
131
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
132
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
133
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
134
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
135
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
136
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
137
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
138
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
139
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
140
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
141
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
142
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
143
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
144
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
145
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
146
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
147
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
148
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
149
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông
150
Bộ môn Truyền thông đa phương tiện – Trường Đại học Công nghệ thông tin và Truyền thông