
Câu lạc bộ Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC
Lập trình C# - Phần 4: C# Nâng cao Trang 1
Lập trình C#
Dịch từ cuốn sách Beginning C Sharp Game Programming
Phần 4: C# Nâng cao
Không gian tên (Namespace)
Không gian tên là một định nghĩa mới trong ngôn ngữ máy tính, nhưng chúng rất hữu dụng, và một số
có thể chứng minh rằng sự tồn tại của chúng là cần thiết.
Một trong những vấn đề lớn nhất của lập trình là quá tải tên (name overlapping). Hãy nói bạn tạo một
rừng các lớp cho chương trình của bạn, và sau đó bạn muốn nhập thư viện của một người nào khác để
giúp cho chương trình của bạn. Điều gì sẽ xảy ra nếu một số lớp của người đó có cùng tên với các lớp
của bạn, nhưng làm các việc khác nhau? Thật không may, điều đó xảy ra rất nhiều.
Lấy ví dụ, cả Direct3D và DirectSound đều có các lớp tên là Device, và bạn rõ ràng không thể có
hai lớp có cùng tên. Không gian tên làm nó trở nên đơn giản, vì vậy bạn có thể ám chỉ các thiết bị
khác nhau như Direct3D.Device và DirectSound.Device.
Bạn có thể nghĩ không gian tên giống như một thành phố. Nếu bạn chỉ nói với một người nào đó rằng
bạn sống ở đường Nguyễn Văn Cừ (chẳng hạn), thì có đến hàng ngàn đường Nguyễn Văn Cừ trong
đất nước. Nếu muốn xác định chính xác bạn sống ở đâu, bạn cần phải nói với người đó cả thành phố
bạn sống nữa. Tạo ra một không gian tên giống như việc cụ thể thành phố và đường của bạn mà trong
đóbạn có thể đặt một số lớp (đường xá) bên trong một không gian tên xác định (thành phố), do đó
chương trình của bạn sẽ được phân chia gọn gàng hơn.
Bạn có thể cho hầu như mọi thứ vào một không gian tên, bao gồm lớp, cấu trúc, trích xuất và cả một
không gian tên khác! Hình 4.1 cho thấy một ví dụ về không gian tên.
Hình 4. 1 Đây là hai không gian tên, System và Chapter04. Trong các không gian tên là các không
gian tên con và các lớp.
Không gian tên System Không gian tên Chapter04
Không gian tên
System.Data
Không gian tên
System.Collections
Lớp
System.Console
Lớp
Chapter04.Spacesation
Lớp
Chapter04.Spaceship

Câu lạc bộ Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC
Lập trình C# - Phần 4: C# Nâng cao Trang 2
Không gian tên rất tuyệt bởi vì bạn có thể phân cấp chúng – bạn có thể cho nhiều không gian tên vào
một không gian tên đang tồn tại. Ví dụ, .NET framework bắt đầu với không gian tên System, với các
không gian tên khác trong nó, như là System.Data hay System.Collections.
Không gian tên phần cấp cho phép bạn tạo cả những hệ thống phân cấp lớn hơn, giống như Việt Nam
tồn tại ở Đông Nam Á, Thành phố Hồ Chí Minh tồn tại trong Việt Nam, và đường Nguyễn Văn Cừ
tồn tại ở Thành phố Hồ Chí Minh.
Tạo Không gian tên
Đây là một số mã mô tả việt sử dụng một không gian tên:
namespace Chapter04
{
class Spaceship
{
// Mã ở đây
};
class Spacestation
{
// Mã ở đây
};
}
Và sau đó, ở bên ngoài không gian tên, bạn sẽ truy cập các lớp đó như thế này:
Chapter04.Spaceship s = new Chapter04.Spaceship();
Một tính năng khác của không gian tên là chúng có thể được phân chia thành nhiều phần. Ví dụ, bạn
có thể có mã như thế này trong một tập tin:
namespace Chapter04
{
class Spaceship
// blah blah
}
và sau đó cho một trạm không gian vào một tập tin khác:
namespace Chapter04
{
class Spacestation
// blah blah
}
Trình dịch C# sẽ tự động ghép các không gian tên cho bạn, vì vậy bạn không phải cho tất cả vào một
tập tin lớn.
Sử dụng Không gian tên
Khi bạn đang ở trong một không gian tên, bạn có thể sử dụng tất cả những gì trong không gian tên đó
mà không cần phải định danh nó. Nếu bạn muốn truy cập lớp Spaceship bên trong lớp
Spacestation trong ví dụ trước tôi cho bạn thấy, thì bạn chỉ cần đánh vào Spaceship và C# sẽ
cho rằng bạn đang nói đến Chapter04.Spaceship bởi vì bạn đang ở trong cùng không gian tên.
Tuy nhiên, nếu bạn đang ở ngoài không gian tên, bạn phải định danh không gian tên bằng cách gõ
Chapter04. phía trước.

Câu lạc bộ Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC
Lập trình C# - Phần 4: C# Nâng cao Trang 3
Đương nhiên, gõ Chapter04.Spaceship nhiều lần có thể gây phiền toái một lúc, đặc biệt là bạn
biết rằng bạn chỉ sử dụng Spaceship trong Chapter04 chứ không phải nơi nào khác. May mắn
thay, bạn được phép nói cho trình dịch C# rằng bạn muốn sử dụng tất cả những gì trong không gian
tên đó, bằng cách sử dụng từ khóa using. Sử dụng nó như thế này:
// Ở phía trên của mã nguồn:
using Chapter04;
// phía sau trong tập tin:
Spaceship s = new Spaceship();
Ghi chú
Từ khóa using có thể được đặt ở một số nơi. Từ khóa không thể được đặt trong một lớp, cấu
trúc hay trích xuất, nhưng nó có thể đặt ở gần như mọi chỗ khác. Thực tế thì nó thường
được đặt ở đầu tập tin mã nguồn, vì vậy bạn sẽ biết những thư viện nào bạn cần ngay tức
thì.
Bí danh của Không gian tên
Không gian tên phân cấp có thể gây phiền toái. Có thể bạn chưa thấy điều đó, nhưng khi bạn đi sâu
vào lập trình DirectX, bạn sẽ hét lên “Microsoft chết tiệt!” ở torng cổ họng của bạn… trừ khi bạn biết
về bí danh cho không gian tên.
Mọi thứ liên quan đến Direct3D đều ở bên trong không gian tên
Microsoft.DirectX.Direct3D. Vậy nếu bạn muốn truy cập một thiết bị Direct3D, bạn sẽ
phải gõ Microsoft.DirectX.Direct3D.Device, đúng không? Thật may mắn, đặt bí danh
cho không gian tên sẽ làm mọi thứ tốt hơn! Về cơ bạn, bạn có thể lấy một không gian tên và nói với
C# cho nó một bí danh.
Đây là một bí danh cho không gian tên Direct3D:
using D3D = Microsoft.DirectX.Direct3D;
D3D.Device d; // thay cho: Microsoft.DirectX.Direct3D.Device d;
Hãy xem, đặt bí danh cho không tên gian làm mọi thứ đơn giản như thế nào?
Tính đa hình (Polymorphism)
Chủ đề về tính đa hình rất là rộng và phức tạp – các trường đại học thường mở cả khóa học về chủ đề
này. Tôi có thể cho bạn cái nhìn thoáng qua về chủ đề này. Nhưng dù sao bạn cũng không cần phải
học bất cứ quá phức tạp về tính đa hình.
Về ngôn ngữ, cái từ đa hình nghĩa là “nhiều dạng”. Trong ngôn ngữ máy tính, tính đa hình cho phép
bạn tương tác với nhiều đối tượng khác nhau mà không cần lo về việc những đối tượng này thực sự là
gì.
Đâu là một ví dụ đời thực của tính đa hình theo nghĩa của ngôn ngữ máy tính, hãy nghĩa về một cái xe
hơi. Bạn vào một chiếc xe, mở nó lên, đạp chân ga, và bạn biết điều gì sẽ xảy ra: Chiếc xe bắt đầu
chạy! Bây giờ ra khỏi chiếc xe đó, vào một chiếc xa hoàn toàn khác, và làm tương tự: Chiếc xe đó
cũng bắt đầu chạy! Cá hai chiếc xa đều có chung giao diện (interface), và bạn thực sự không cần quan
tâm làm sao động cơ hoạt động ở bên dưới. Cho dù bạn đang lái một cái xe bốn xi lanh bình thường,
một chiếc xe đua tám xi lanh, hay một chiếc xa hơi điện, bạn biết rằng khi đạp ga, chiếc xe bắt đầu

Câu lạc bộ Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC
Lập trình C# - Phần 4: C# Nâng cao Trang 4
chạy. Đây là ví dụ tốt nhất cho tính đa hình. Máy vi tính gọi một đối tượng làm việc, và đối tượng đó,
không quan tâm nó là gì, sẽ làm việc.
Đa hình cơ bản
Hãy nói bạn có một cây phân cấp rất cơ bản: một gốc và hai con. Gốc là Spaceship (tàu vũ trụ) và
các con là CombatShip (tàu chiến) và CargoShip (tàu chở hàng), như hình 4.2.
Hình 4. 2 Một cây phân cấp đơn giản
Bạn có thể chơi với chúng như bình thường bạn vẫn làm:
Spaceship s = new Spaceship();
CargoShip c = new CargoShip();
Đương nhiên, không có gì mới. Nhưng điều đặc biệt là một CargoShip là một Spaceship cho
phép bạn thực hiện một vài thủ thuật. Nhìn dòng mã này:
Spaceship s = new CargoShip();
Đoạn mã đó hoàn toàn hợp lệ. Dù sao, một CargoShip là một SpaceShip, vì vậy nó sẽ có nghĩa
khi làm một Spaceship trỏ tham chiếu tới một CargoShip, đúng không?
Có một giới hạn cho điều này: Tham chiếu SpaceShip sẽ không cho phép truy cập những phần
riêng nào trong lớp CargoShip mà nó không thừa kế từ SpaceShip. Giả sử rằng SpaceShip có
hàm Refuel, và CargoShip có hàm LoadCargo, thì hãy nhìn vào đoạn mã ví dụ sau:
Spaceship s = new CargoShip();
s.Refuel(); // ok
// s.LoadCargo(); // LỖI BIÊN DỊCH. SpaceShip không được LoadCargo
CargoShip c = (CargoShip)s; // vì vậy, chuyển nó thành CargoShip
c.LoadCargo(); // ok

Câu lạc bộ Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC
Lập trình C# - Phần 4: C# Nâng cao Trang 5
Bất kể khi nào mà bạn có một tham chiếu đến một lớp, bạn chỉ có thể truy cập vào những tính năng
của lớp xác định, bất kể nếu đối tượng thực sự có thể hỗ trợ nhiều tính năng hơn nữa.
Ghi chú
Ghi chú rằng bạn không thể sử dụng tính đa hình theo chiều ngược lại. Nếu bạn cố gắng viết
CargoShip c = new SpaceShip();, bạn sẽ có một lỗi biên dịch. Một SpaceShip không
phải là một CargoShip.
Hàm ảo (Virtual Function)
Một trong những khía cạnh quan trọng nhất của tính đa hình là ý tưởng về một hàm ảo. Một hàm ảo
cơ bản cho phép bạn định nghĩa một hàm trong lớp ốc và sau đó có thể thay đổi nó. Hãy nói như thế
này, mọi tàu vũ trụ lo việc bị bắn bởi laser một cách khác nhau, vì vậy bạn định nghĩa việc xử lý việc
đó trong lớp SpaceShip gốc. Sau đó, bạn đề nghị rằng tàu chiến có thể lo việc bị trúng đạn một
cách khác bởi vì chúng có giáp tốt hơn.
Hàm ảo cho phép bạn giải quyết tình huống này một cách dễ dàng, như bạn sẽ thấy trong những mục
sau.
Khi không có Hàm ảo
Đây là một số mã sẽ làm rõ cái gì sẽ xảy ra trong ví dụ của tôi mà không có hàm ảo:
class Spaceship
{
public void LaserHit()
{
// Thiệt hại nhiều
}
}
class CombatShip : Spaceship
{
public void LaserHit()
{
// Thiệt hại ít
}
}
Điều mà tôi vừa làm là tạo một lớp SpaceShip và mặc định làm cho tàu bị nhiều thiệt hại khi bị
trúng laser. Tôi muốn CombatShip sẽ bị thiệt hại ít hơn bởi vì chúng có nhiều giáp hơn, vì vậy tôi
tạo một hàm LaserHit mới thực hiện thiệt hại ít hơn.
Đoạn mã làm chính xác những gì bạn nghĩ là:
Spaceship s = new Spaceship();
CombatShip c = new CombatShip();
s.LaserHit(); // Thiệt hại nhiều
c.LaserHit(); // Thiệt hại ít
Không có thủ thuật gì ở đây. Nhưng về các mã này thì sao?
Spaceship s = new Spaceship();
Spaceship c = new CombatShip();
s.LaserHit(); // Thiệt hại nhiều
c.LaserHit(); // Thiệt hại nhiều... tại sao?

