Gía trị với Tham chiếu Có những cách khác nhau để một trình biên dịch nói về dữ liệu, và C# có hai cách. Mọi kiểu dữ liệu trong C# đều rơi vào một trong hai loại: Kiểu giá trị (Value type) Kiểu tham chiếu (Reference type)
Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Lập trình C# Dịch từ sách Beginning C Sharp Game Programming Phần 3: Giới thiệu Lớp Gía trị với Tham chiếu Có cách khác để trình biên dịch nói liệu, C# có hai cách Mọi kiểu liệu C# rơi vào hai loại: Kiểu giá trị (Value type) Kiểu tham chiếu (Reference type) Tơi giải thích điểm khác loại mục sau Kiểu liệu Một kiểu giá trị thường miếng nhỏ liệu mà hệ thống dành thời gian để xếp Bạn sử dụng kiểu giá trị Phần với tất kiểu liệu số xây dựng sẵn Mọi thứ liệt kê bảng 2.1 – int, float vâng – kiểu giá trị Ghi Kiểu giá trị tạo ngăn xếp hệ thống (system stack) Bạn khơng cần thiết để biết gì, bạn cảm thấy thú vị, khuyên bạn nên tự nghiên cứu Chủ đề vượt ngồi phạm vi sách này, tơi khơng đủ chỗ để giải thích đây, giúp bạn hiểu xác máy vi tính hoạt động, ảnh hưởng đến việc tạo chương trình bạn nhanh hiệu Kiểu giá trị đơn giản minh bạch để sử dụng, đoạn mã sau: int x = 10, y = 20; x = y; // Giá trị y chép vào x y = 10; // y gán 10 Bên cạnh kiểu đượcdựng sẵn, cấu trúc (structure) kiểu giá trị, đề cập sau phần Kiểu tham chiếu Kiểu tham chiếu hoàn toàn khác biệt so với kiểu giá trị Lớp, khác với cấu trúc, luôn kiểu tham chiếu Kiểu tham chiếu, thay lưu liệu cách trực tiếp, lại lưu địa chỉ, địa trỏ tới liệu thật nơi máy vi tính Xem hình 3.1 Khai báo Kiểu Tham chiếu Một điểm khác lớn kiểu giá trị tham chiếu cách mà bạn khai báo cho Một kiểu tham chiếu phải tạo từ khóa new (giả sử có lớp tên Foo): Foo x = new Foo(); Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Trơng nhiều việc cần làm, bạn quen với Cơ bản, đoạn mã thực hai cơng việc Đó là: Tạo kiểu tham chiếu tên x, Tạo đối tượng Foo đống liệu trỏ x đến int x Kiểu giá trị 10 Int32 y Kiểu tham chiếu địa liệu 10 Hình 3.1 Kiểu giá trị lưu trực tiếp, kiểu tham chiếu lưu địa trỏ tới liệu thật Ghi Đống liệu (Heap) phần khác máy vi tính để lưu trữ liệu Tơi khơng có đủ chỗ để giải thích đây; vài thứ khác bạn nên nghiên cứu cho riêng bạn nên bạn cảm thấy thú vị Đương nhiên, bạn không cần phải thực chúng lượt Bạn dễ dàng tách chúng này: Foo x; x = new Foo(); Nó tùy thuộc vào bạn Chơi đùa với Tham chiếu Và lúc để chơi đùa với tham chiếu, thứ mà bạn chưa gặp trước Không may cho bạn, tham chiếu không hoạt động theo cách kiểu liệu, mà gây bối rối chút Đây phần mà tham chiếu khó hiểu chút Bạn hồn tồn phải nhớ điều lúc môi nơi sử dụng tham chiếu, cịn khơng chươn trình bạn trở thành mớ hỗn độn Ví dụ, thử đốn xem đoạn mã làm gì: Foo x = new Foo(); Foo y = new Foo(); y = x; // Thay đổi y Bạn có nghĩ sau đọan mã thực thi, x giữ nguyên trạng thái ban đầu y thay đổi, không ? Sai !!! Chúng thay đổi Hãy nghe tơi – có chút khó khăn để nhìn nhận nó, có nghĩa Sơ đồ giúp, nhìn hình 3.2 Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM Foo x = new Foo(); Foo y = new Foo(); y = x; // Thay đổi y LHPSC x liệu y liệu x liệu y liệu x liệu thay đổi y liệu Hình 3.2 Gán x cho y làm y trỏ tới liệu x, không thực chép giá trị bạn mong đợi Cơ bản, dòng thật làm rối tung thứ lên: y = x; Điều thật hồn thành? Bạn muốn chép giá trị từ x sang y, điều khơng xảy Thay vào đó, chúng kiểu tham chiếu, máy vi tính trỏ y vào liệu mà x trỏ vào Vì x y trỏ đến liệu nhớ, thực thao tác y làm giống x Bộ thu gom rác Trong ví dụ hình 3.2, bạn ghi nhận y giành lấy vùng nhớ, sau bị bỏ qua gán x cho y Vậy điều xảy vùng nhớ mà y trỏ tới trước đó? Trong ngơn ngữ cũ, C, vùng nhớ bị mãi Bạn tạo gọi trỏ treo (dangling pointer), trỏ giống tham chiếu; máy vi tính biết vùng nhớ sử dụng, chương trình bạn quên đâu, bạn khơng lấy lại vùng nhớ trừ bạn tắt chương trình C# giải vấn đề thu gom rác Mỗi lần bạn tạo mảnh liệu C#, NET runtime theo dõi chương trình bạn trỏ đến liệu lần, số lần 0, thu gom rác phát giải phóng vùng nhớ để sử dụng cho thứ khác Khơng thể có rị rỉ nhớ C# null Có giá trị đặc biệt mà bạn sử dụng với kiểm tham chiếu; gọi null Giá trị null có nghĩa “chẳng có gì” Nếu bạn đặt tham chiếu đến null, bạn nói với máy vi tính tham chiếu chẳng trỏ tới đâu Những ngơn ngữ cũ sử dụng giá trị để biểu thị nó, null dễ đọc Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Cơ Cấu trúc (Structure) Lớp (Class) Trong ngày đầu ngôn ngữ máy tính, ngơn ngữ lập trình đơn giản, bạn tạo số lượng biến giới hạn Điều rõ ràng làm cho chương trình hạn chế tồi tệ Ví dụ, bạn tạo chương trình ngơn ngữ lập trình cũ: int int int int int int SpaceshipArmor; SpaceshipPower; SpaceshipFuel; EnemyArmor; EnemyPower; EnemyFuel; Điều rối tung thứ nhanh, làm cho khó để quản lý mã bạn Sử dụng lớp cấu trúc làm cho sống bạn đơn giản đóng gói liệu vào gói liệu “rất dễ sử dụng” Tạo Lớp Cấu trúc Về bản, ý tưởng đằng sau lớp cấu trúc tạo kiểu đối tượng cho riêng bạn đối tượng có sẵn Một cấu trúc (structure) kiểu liệu mà giữ liệu khác bên trong, cho phép bạn xây dựng kiểu liệu riêng bạn Ví dụ, cấu trúc mô tả đối tượng tàu vũ trụ đơn giản C#: struct Spaceship { public int fuel; public int armor; public int power; } Ghi Từ khóa public nói cho trình biên dịch hàm đâu đầu truy cập liệu bên cấu trúc Bạn lo việc này; sâu vào điều mục sau Nếu public bị bỏ đi, máy vi tính xem bạn khơng muốn thứ bên ngồi truy cập Ghi Để tạo lớp, đơn giản thay từ khóa struct thành class ví dụ trước Và bậy giờ, bên chương trình bạn, bạn tạo biến tàu vũ trụ cho riêng bạn: Spaceship player; Spaceship enemy; player.fuel = 100; enemy.fuel = 100; Điều đơn giản, phải không? Sự khác Cấu trúc Lớp Trong C#, có vài điều khác cấu trúc lớp Cấu trúc thường đơn giản khơng có nhiều thứ phức tạp bên chúng Cấu trúc thường nhỏ lớp, C# tạo cấu trúc kiểu giá trị (nghĩa chúng ln ln tạo ngăn xếp) Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Lớp, khác với cấu trúc, luôn kiểu tham chiếu, tạo đống (heap), thay ngăn xếp Lớp có nhiều thứ mà cấu trúc khơng có, tơi giải thích thứ gặp Đưa Hàm (Function) Lớp Cấu trúc Lớp cấu trúc tính lưu trữ liệu, mà chúng cịn thực vài phép tính nữa, bạn cho chúng khả Ví dụ, bạn muốn đặt lại liệu tàu thành 100 cách nhanh chóng; khơng có hàm, trông giống đoạn mã sau: player.fuel = 100; player.armor = 100; player.power = 100; Rõ ràng, điều thứ bạn làm lần, thay vào khơng cho vào hàm, nên lớp Spaceship trông (phần tô đậm): struct Spaceship { public int fuel; public int armor; public int power; public void Recharge() { fuel = 100; armor = 100; power = 100; } } Bây bạn cần gọi hàm Recharge tàu vũ trụ bạn bạn muốn đặt lại tất biến: player.Recharge(); Trả giá trị Các hàm không trhực tác vụ, mà chúng cịn trả giá trị Ví dụ, nói bạn có tàu vũ trụ; bạn biết nhiên liệu lượng có, bạn không thực chắn thời gian mà lượng cung cấp hết Để tính tốn nó, bạn cần thiết lập cơng thức – nói đơn vị lượng trì hai giờ; bạn muốn tìm xem cịn thời gian với số lượng lại, bạn làm điều này: int hoursleft = player.power * 2; Đó cách để giải vấn đề, khơng thực giải pháp tốt Về sau, bạn đề nghị đơn vị lượng tương đương với ba thay hai Để thay đổi điều này, bạn phải dò hết lại mã bạn tìm xem chỗ bạn sử dụng số thay số Khơng vui chút Vì vậy, đưng trình vào hàm! int HoursofPowerLeft() { return power * 2; } Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Ta-da! Chữ int trước tên hàm nói cho máy vi tính biết kiểu liệu trả từ hàm, bạn sử dụng từ khóa return để trả giá trị Nếu bạn khơng muốn trả gì, sử dụng void, bạn thấy trước Bạn nên ghi mệnh đề return làm cho hàm kết thúc Nếu bạn nhìn vào đoạn mã sau, bạn thấy số chúng không thực thi: int Function() { return 0; int x = 10; // Dịng khơng thực thi } Ghi Bạn nên ghi trình biên dịch C# đủ thơng minh để nhận đoạn mã khơng thực thi, hét lên bạn viết mã kiểu Bạn có nhiều câu lệnh return mã bạn: int Function() { if( something ) return 0; return 1; } Trong đoạn mã này, something true, trả về; khơng, trả (và thốt, tất nhiên) Thơng số Bạn đưa hàm vài thơng số Ví dụ tơi tính thời gian lại dựa số lượng lại phép tính đơn giản khơng sử dụng tham số nào, tơi thay đổi điều làm cho linh hoạt Điều xảy lượng giảm phụ thuộc vào số tác nhân bên ngồi, có phóng xạ hệ thống? Hãy nói mức độ phóng xạ thấp, lượng bị tiêu tốn Vì vậy, bạn viết lại hàm trước với ý tưởng này, bạn có: int HoursofPowerLeft( int radiationlevel ) { return (power * 2) / radiationlevel; } Nếu mức độ phóng xạ (tơi hồn toàn hư cấu liệu đo đây; xem có nghĩa) lượng bạn 100, số cịn lại 200 Nếu mức độ phóng xạ 2, bạn cịn 100 giờ; 3, bạn cịn 66 Bạn gọi hàm này: int hoursleft = player.HoursofPowerLeft( ); Nhiều thông số Sẽ có lúc bạn muốn đưa nhiều thơng số, C# cho phép bạn làm điều đó: int Function1( int parameter1, float parameter2, double parameter3 ) Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Thông số giá trị vs Thông số tham chiếu Đây phần lắt léo chút Hãy nói bạn có lớp với hai thơng số trơng này: class MyClass { public void Function1( int parameter ) { parameter = 10; } public void Function2() { int x = 0; Function1( x ); // X mấy? } } Vậy, x sau đoạn mã mấy? Là 10? Câu trả lời bạn đưa x vào giá trị Điều có nghĩa máy vi tính lấy giá trị x, chép nó, vào đặt vào biến tên parameter; bây giờ, parameter thay đổi, chẳng có xảy với x Vậy bạn đưa vào tham chiếu? Chỉ cần làm hai việc Thứ nhất, thay đổi khai báo Function1: public void Function1( ref int parameter ) Thứ hai, thay đổi cách gọi hàm này: Function1( ref x ); Và bạn đưa vào tham chiếu x, giá trị x thay đổi Bạn nên ghi lớp ln ln đưa vào tham chiếu Ví dụ: public void Function1( Int32 parameter ) Trong hàm này, Int32 bạn đưa vào luôn tham chiếu, khơng phải giá trị Q tải hàm Bạn có lớp hay cấu trúc với vài hàm tên Điều nghe ngu ngốc, hoạt động tốt đời thực Ví dụ, nói bạn có hai cách khác để tính tốn qng đường tàu vũ trụ, đưa lượng nhiên liệu cịn lại nói; cách lấy số hàng hóa mà tàu chở, cách khác bỏ qua số hàng hóa cho bạn kết “điều khiện tốt nhất” Bạn tạo hàm này: public int DistanceLeft( int cargoweight ) // với hàng hóa { // Tính toán } public int DistanceLeft() // tối ưu, khơng hàng hóa { // Tính tốn } sau bạn gọi chúng này: Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC int distance; distance = player.DistanceLeft( 100 ); distance = player.DistanceLeft(); Bạn tải hàm bao nhiều lần tùy thích; điều hạn chế hàm tải phải có chữ ký khác Chữ ký định nghĩa thông số mà bạn đưa vào, giá trị trả Vì bạn làm này: void Function1(); void Function1( int p1 ); void Function1( float p1, int p2 ); Và bạn làm này: int Function1(); void Function1( int p1 ); float Function2( float p1, int p2 ); Nhưng hồn tồn khơng thể làm này: int Function1(); float Function1(); // chữ ký! LỖI! Hàm xây dựng (Constructor) Hàm xây dựng thứ có ích hầu hết ngơn ngữ lập trình đại Chúng cho phép bạn tự động khởi tạo lớp cấu trúc bạn Quay ngày xa xưa, bạn tạo lớp hay cấu trúc mới, bạn thật khơng biết bên Như bạn thấy, máy vi tính khơng xóa nhớ, bạn ngưng sử dụng mảnh nhớ, máy vi tính đánh dấu khơng sử dụng nữa, vị trí danh mục tái sử dụng, với liệu mà lưu trước Điều có nghĩa bạn thường có cấu trúc đầy liệu rác khơng có nghĩa, gây nhiều vấn đề bạn bắt đầu sử dụng cấu trúc mà khơng kiểm tra có giá trị hợp lệ hay không Hàm xây dựng hàm mà C# gọi tự động bạn tạo lớp Hàm xây dựng mặc định (Default Constructor) Đây ví dụ đơn giản hàm xây dựng lớp: class Spaceship { public int fuel; public Spaceship() // Hàm xây dựng mặc định { fuel = 100; } } Đoạn mã đậm gọi hàm xây dựng mặc định Khi bạn tạo tàu vũ trụ mới, hàm Spaceship (ghi có tên với lớp) tự động gọi Mã tạo tàu vũ trụ với nhiên liệu 100: Spaceship s = new Spaceship(); Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Hàm xây dựng không mặc định (Non-default Constructor) Như hàm bình thường, hàm xây dựng có phiên tải, mà hữu dụng bạn cần cung cấp thêm liệu tạo lớp Đây ví dụ cho thấy làm để tạo hàm xây dựng không mặc định đưa vào biến lượng nhiên liệu để gán cho tàu vũ trụ: public Spaceship( int p_fuel ) { fuel = p_fuel; } Về bạn tạo tàu vũ trụ, bạn gọi hàm xây dựng này: Spaceship s = new Spaceship( 50 ); // tạo tàu với 50% nhiên liệu Bạn tạo hàm xây dựng tùy thích, miễn chúng có chữ ký khác Cấu trúc Hàm xây dựng Cấu trúc có hàm xây dựng, có điều: Các cấu trúc khơng thể có hàm xây dựng mặc định Microsoft tuyên bố cấu trúc phải nhẹ Vì cấu trúc có hàm xây dựng khơ mặc định, khơng thể có hàm xây dựng mặc định Hàm phá hủy (Destructor) Nếu hàm xây dựng gọi lớp tạo, hàm phá hủy gọi lớp bị loại bỏ Trong ngôn ngữ cụ C++, hàm phá hủy quan trọng bạn ln ln phải giải phóng nhớ mà bạn không sử dụng Nhưng từ xuất thu gom rác, hàm phá hủy gần theo lối khủng long Chúng thật không sử dụng nhiều nữa, thêm vào để đề phóng Ví dụ Một hàm phá hủy trông giống này: class MyClass { ~MyClass() { // Mã } } Trong ngôn nhữ giống C++, nơi mà bạn tạo hàm tự động dọn dẹp nhớ cho bạn có yêu cầu Nhưng NET runtime lo tất vấn đề nhớ cho bạn, bạn thực không cần phải làm Sự phá hủy bị trì hỗn Một điều khác hàm phá hủy chúng không gọi tức thời Ví dụ, nhìn đoạn mã này: Spaceship s = new Spaceship(); // tàu vũ trụ s = null; // tham chiếu đến tàu vũ trụ bị Trong đoạn mã này, tàu vũ trụ tạo tham chiếu đến gán cho s Tuy nhiên, dòng tiếp theo, tham chiếu đặt null, hệ thống giữ vùng nhớ thời gian dài sau bạn đặt lại tham chiếu Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Do đó, vấn đề với hàm phá hủy bạn thật chúng gọi Bạn làm lớp bị phá hủy tức thời, đừng nói bị phá hủy bạn xóa hết tham chiếu Cịn nhiều thứ khác hàm phá hủy, dù bạn không sử dụng chúng nhiều Nên đừng bận tâm tới chúng Ghi Cấu trúc khơng có hàm phá hủy chúng thứ nhẹ Các thủ thuật nâng cao Lớp Hiện nay, bạn quen với lớp bạn tạo để thực tác vụ nhỏ lưu trữ liệu thực phép tính đơn giản Tuy nhiên, lớp cịn có nhiều điều mà bạn chưa thấy Cơ thừa kế (Inheritance) Một ưu điểm lớn ngôn ngữ lập trình hướng đối tượng thừa kế Tơi không sâu vào chủ đề thừa kế, bạn nên hiểu sử dụng thừa kế sau đọc xong sách Sự thừa kế cho phép bạn tạo chương trình bạn theo cách thực tế, khả phân cấp làm cho lớp bạn giống đối tượng đời thực Cách dễ để nghĩ thừa kế nghĩ phân lớp động vật Một ví dụ Thừa kế Nếu bạn nhớ mơn sinh học thời trung học, bạn biết thú có số đặc điểm chung, sinh có tim bốn ngăn Trong chương trình máy vi tính, bạn tạo lớp thú bạn cho đặc điểm Nhưng dù loài thú chia chung số đặc điểm, chúng lại không chia sẻ hết đặc điểm Con người có hai chân, cừu có bốn – rõ ràng vậy, bạn không tể sử dụng lớp thú để biểu diễn người lẫn cừu Thật ngu ngốc tạo hai lớp hoàn toàn khác để biễn diễn cho người cừu Đoạn mã bạn viết biểu diễn đặc điểm chung người cừu bị lặp lại hai lớp Hình 3.3 biểu diễn tình Một cách TỒI TỆ Lớp Cừu Đoạn mã thú Lớp Người Đoạn mã lặp lại Đoạn mã cừu Đoạn mã thú Đoạn mã người Hình 3 Hình cho thấy cách sai để tạo hệ thống người/cừu Lập trình C# - Phần 3: Giới thiệu Lớp Trang 10 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Cảnh báo Lặp mã luôn ý tưởng tồi Một ngày đó, bạn cần thay đổi đoạn mã bạn viết, bạn có đoạn mã chỗ khác, tơi đảm bảo bạn quên vài nơi có hai phiên khác chạy mã bạn Đây lúc mà thừa kế vào làm việc Thừa kế cho phép bạn tạo lớp tự động sử dụng tất đặc điểm lớp khác Điều gọi mối quan hệ (con cừu thú) Ví dụ 3.4 cho thấy kế thừa sử dụng Một cách TỐT Lớp Thú Đoạn mã thú Lớp Cừu Lớp Người Đoạn mã cừu Đoạn mã người Hình Bạn sử dụng thừa kế để chia đoạn mã chung lớp cách Sử dụng Thừa kế Thừa kế dễ sử dụng C# Đầu tiên bạn cần tạo lớp gốc (base class), đứng thừa kế bạn (lớp thú phần trước ví dụ) Hãy nói bạn muốn tạo lớp tàu vũ trụ gốc, lớp mô tả đặc điểm chung tất tàu vũ trụ tồn Như tất tàu vũ trụ có nhiên liệu nó, bạn tạo lớp gốc bạn: class Spaceship { public int fuel; }; Ghi Nhớ đầu ví dụ đơn giản, với đoạn mã tơi thiểu hóa để mơ tả khái niệm cho bạn Tôi cố gắng không làm bạn lầm lẫn khái niệm Vậy bạn có tàu vũ tàu, tất chúng có nhiên liệu Bây bạn muốn tạo tàu chiến, chúng có vũ khí: class Warship : Spaceship { public int weapons; Lập trình C# - Phần 3: Giới thiệu Lớp Trang 11 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC }; Bạn nói với trình biên dịch tàu chiến tàu vũ trụ đặt dấu hai chấm tên lớp tên lớp mà bạn thừa kế Ví dụ, bạn tạo tàu chở hàng: class Cargoship : Spaceship { public int storage; }; Bây bạn sử dụng đặc điểm mà bạn vừa thêm vào đặc điểm lớp gốc nữa: Warship w = new Warship(); w.weapons = 100; // Đặc điểm w.fuel = 100; // Đặc điểm thừa kế từ Spaceship Cargoship c = new Cargoship(); c.storage = 100; // Đặc điểm c.fuel = 100; // Đặc điểm thừa kế từ Spaceship Và điều tốt đẹp với thừa kế Mức độ Truy cập Giấu Dữ liệu Cho đến nay, bạn thấy từ public đoạn mã ví dụ, tơi chưa giải thích nghĩa Về bản, bạn nói điều public, bạn nói với trình biên dịch thứ truy cập chúng Nếu bạn cho lớp số nguyên public, thứ đọc số ngun thay đổi Điều tất ngơn ngữ lập trình sử dụng ý tưởng giấu liệu xuất Ý tưởng đằng sau việc Giấu Dữ liệu Giấu liệu khái niệm cho phép bạn giấu liệu từ phần khác chương trình bạn Bạn thắc mắc bạn muốn giấy liệu làm quái Câu trả lời cần lời giải thích Bạn không muốn đụng đến liệu bạn; bạn làm điều gây hại đến Lấy ví dụ tàu vũ trụ Trong tàu vũ trụ, nhiên liệu giảm cịn 0, động tắt Bây tưởng tượng tất nơi mã bạn mà bạn có hàm chỉnh sửa số nhiên liệu tàu vũ trụ bạn Có thể tàu bạn rị rỉ số nhiên liệu trúng đạn Hay bạn có gói nhiên liệu để thêm số nhiên liệu vào bình chứa Có tới hàng triệu khả Có nhiều nơi tay đổi số nhiên liệu, tất chúng cần phải tắt động nhiên liệu xuống 0, điều điều tồi tệ Hơn nữa, cố gắng cho nhiều nhiên liệu vào bình chứa vượt q dung tích bình, điều mà bạn khơng muốn xảy Vì vậy, bạn cho bất cừ hàm chỉnh sửa liệu bạn, hàm làm chúng rối lên cách sơ ý, điều chắn khơng phải điều tốt Vì bạn cần tác vụ kiểm tra Ví dụ, bạn có sáu đạon mã khác chỉnh sửa số nhiên liệu bạn, đoạn mã cần phải kiểm tra nhiên liệu rơi xuống 0, cần phải tắt động Hay đạon mã cần kiểm tra để chắn số nhiên liệu không vượt lượng tối đa, khơng tràn ngồi bình Lập trình C# - Phần 3: Giới thiệu Lớp Trang 12 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Có nhiều đoạn mã bị lặp lại tất nơi, làm cho chương trình bạn phức tạp tồi tệ Vì ý tưởng tệ hại phần khác chương trình đụng vào liệu bạn Mức độ truy cập C# có định nghĩa số mức độ truy cập Bạn thấy số chúng, public (công khai) Nhưng bạn đốn, public nghĩa truy cập đặc điểm Hai mức độ truy cập phổ biến khác protected (được bảo vệ) private (riêng tư) Truy cập private nghĩa khơng phần mã truy cập đặc điểm trừ từ lớp Các phần khác khơng truy cập, lớp thừa kế Hãy xem ví dụ này: class Spaceship { private int fuel; }; class Warship : Spaceship { public void SomeFunction() { fuel = 10; // Lỗi, truy cập “fuel” } }; Spaceship s = new Spaceship(); s.fuel = 10; // Lỗi, truy cập “fuel” Bên warship, hàm SomeFunction cố gắng truy cập vào fuel Như từ trước ta nói, bạn khơng có vấn đề làm này; sau tất cả, tàu chiến tàu vũ trụ, có nhiên liệu bị chỉnh sửa Nhưng nhiên liệu private, tàu chiến khơng thể truy cập Nhiên liệu đó, đương nhiên, tàu chiến khơng cho phép đụng vào chúng Đoạn mã khác không phép đụng vào chúng; liệu giấu Ghi Nếu bạn quên gán mức độ truy cập vào hàm hay biến, trình biên dịch C# tự động cho sử dụng mức truy cập private Mức độ truy cập protected tương tự private, với thay đổi nhỏ: Bất thứ protected giấu với đoạn mã bên ngồi lớp đó, lớp thừa kết từ lớp gốc truy cập đặc điểm Hãy xem ví dụ sau: class Spaceship { protected int fuel; }; class Warship : Spaceship { public void SomeFunction() { fuel = 10; // Bây ổn, protected } }; Spaceship s = new Spaceship(); Lập trình C# - Phần 3: Giới thiệu Lớp Trang 13 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC s.fuel = 10; // Lỗi, truy cập “fuel” Ví dụ giống với trước, lần nhiên liệu protected tàu chiến truy cập Các Thành phần Tĩnh (Static Member) Cho đến giờ, tất thành phần bạn thấy lớp thành phần cá biệt (instance member) Một thành phần cá biệt phần lớp tồn với cá thể lớp Nếu bạn có hai tàu vũ trụ, tàu vũ trụ có số nguyên biễu thị nhiên liệu, bạn có hai số nguyên, số cho tàu Mặt khác, bạn có thành phần tĩnh Một thành phần tĩnh mẩu liệu (hay hàm) chia sẻ với tất cá thể lớp, thay lặp lại với cá thể Hình 3.5 ví dụ Dữ liệu tĩnh Spaceship.count Định nghĩa Spaceship static int count Dữ liệu cá biệt int fuel int cargo Cá thể Spaceship Cá thể Spaceship Cá thể Spaceship fuel fuel fuel cargo cargo cargo Hình Sự khác tĩnh liệu cá biệt Dữ liệu Tĩnh Hãy nhìn đoạn mã sau đây: class Spaceship { public static int count; public int fuel; public int cargo; }; Đoạn mã tạo lớp định nghĩa cho tàu vũ trụ, với tàu có nhiên liệu hàng hóa, định nghĩa lớp theo dõi số nguyên tên count Bạn trut cập số nguyên lúc gọi dịng mã (hay tương tự): Spaceship.count = 10; Bạn khơng cần có cá thể tàu vũ trụ để sử dụng biến này; ln ln tồn Nó khơng thuộc tàu vũ trụ cả; đầu sử dụng Tĩnh cách dễ dàng để có biến Lập trình C# - Phần 3: Giới thiệu Lớp Trang 14 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC chức với biến toàn cục C hay C++; gọn gàng hơn, theo quan điểm thiết kế Hàm Tĩnh Hàm tĩnh Về bản, hàm tĩnh gọi mà khơng cần cá thể để hoạt động Ví dụ: class Spaceship { public static void FunctionA() { // Làm } }; Và bạn gọi hàm cách gọi sau: Spaceship.FunctionA(); Bạn không cần cá thể tàu vũ trụ để gọi hàm Chú ý Hàm tĩnh hoạt động với liệu cá biệt mà khơng có cá thể thực để hoạt động Nó rõ ràng bạn nghĩ nó, số người khơng hiểu điều Thuộc tính (Property) Tơi nói với bạn trước giấu liệu điều tốt Trong thực tế, sử dụng số thủ thuật tồi tệ để tạo ví dụ dễ hiểu sách Nói chung, bạn nên khơng để liệu public lớp bạn Sự cám dỗ cho phép truy cập trực tiếp đến liệu bạn mạnh mẽ, sớm hay muộn bạn rơi vào vấn đề kể cho bạn trước Và bạn bắt đầu có lỗi từ khơng đâu, tơi khơng muốn bạn đến than thở với tơi Hàm truy cập (Accessors) Nói chung, khứ, phương pháp ưa thích để làm cho liệu truy cập mà khơng cần no public sử dụng hàm truy cập Để làm vậy, bạn tạo hai hàm cho biến: để lấy giá trị, để đặt giá trị Như này: class Spaceship { protected int fuel; public int GetFuel() { return fuel; } public void SetFuel( int p_fuel ) { fuel = p_fuel; } }; Và bạn truy cập nhiên liệu này: Spaceship s = new Spaceship(); s.SetFuel( 10 ); int f = s.GetFuel(); Ghi Phương thức xem xét anh tồn bạn thay đổi nhiên liệu truy cập sau mà khơng cần làm rối tung đoạn mã khác Ví dụ, bạn đề nghị ngày mà bạn không muốn người có khả đặt số nhiên liệu 0, bạn thay đổi hàm SetFuel để kiểm tra cách tự động Lập trình C# - Phần 3: Giới thiệu Lớp Trang 15 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Điều này, theo quan điểm kỹ thuật, an tồn Nhưng theo quan điểm “tơi đánh máy hàng tơi muốn nhà”, giải pháp thật vớ vẩn Có nhiều mã thêm vào để làm cho chương trình bạn “an tồn hơn” Giải pháp C#: Thuộc tính Để sửa vấn đề “đánh máy nhiều” tồn ngôn ngữ cũ, C# giới thiệu định nghĩa gọi thuộc tính Một thuộc tính cho phép bạn dử dụng mã để làm cho biến bạn truy cập hơn, giữ bảo vệ hàm truy cập Đây ví dụ: class Spaceship { protected int fuel; public int Fuel { get { return fuel; } set { fuel = value; } } }; Đoạn mã đậm vùng thuộc tính Về tơi tạo thuộc tính có tên Fuel (chữ F hoa), mà hành động giống hệt mẩu liệu ngoài: Spaceship s = new Spaceship(); s.Fuel = 10; int f = s.Fuel; Vậy thực diễn đằng sau màn, hàm thuộc tính get set gọi, thực thi mã chúng Bạn làm thứ bạn muốn cà hai hàm thuộc tính, bạn thường làm nhiều điều hàm set Ví dụ, bạn chắn khơng đặt số nhiên liệu tàu vũ trụ thay đổi hàm Fuel.set này: public int Fuel { get { return fuel; } set { if( value < ) fuel = 0; else fuel = value; } } Và bây giờ, bạn thực mã này: s.Fuel = -10; hàm set tự động đặt nhiên liệu xuống thay -10 Chú ý Thuộc tính khơng có phát đệ quy vơ hạn, đơi gây phiền tối Ví dụ, bạn vơ tình đánh Fuel = value thay fuel = value bên hàm set, máy vi tính tự động gọi hàm set lần nữa, tiếp tục chương trình bị treo Hãy cẩn thận với điều Lập trình C# - Phần 3: Giới thiệu Lớp Trang 16 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Trích xuất (Enumeration) Một đặc điểm C# (và ngôn ngữ khác) làm cho đời bạn đơn giản định nghĩa kiểu trích xuất (enumeration) Đã lần bạn viết chương trình mà bạn có số kiểu liệu số, kiểu liệu tùy chỉnh? Hãy tưởng tượng này: Bạn làm hệ thống đơn giản mà tàu vũ trụ trị chơi bạn biết trạng thái – di chuyển vòng quanh, dừng, hau chiến đấu với số kẻ thú Bạn đề nghị sử dụng số nguyên, này: class Spaceship { int state; // Mã khác }; OK, trạng thái di chuyển, dừng lại, chiến đấu Nghe hợp lý, không? Không! Điều tồi tệ Đừng làm này! Bạn nhớ số có nghĩa Giải pháp C# trích xuất Một kiểu trích xuất đóng gói nhóm tên vào kiểu mà dễ dàng tham chiếu đọc hiểu Đây kiểu trích xuất biểu diễn trạng thái tàu: enum SpaceshipState { moving, stopped, battle }; Mã tạo kiểu trích xuất có tên SpaceshipState, mà có tổng cộng ba giá trị khác nhau: moving, stopped battle Bạn sử dụng này: SpaceshipState s; s = SpaceshipState.moving; if( s == SpaceshipState.battle ) // Mã // tiếp tục Trích xuất thực số nguyên; bạn không cần phải nghĩ chúng thế, bạn bạn muốn Thơng thường, trích xuất gán giá trị 0, chúng tăng int i = i = i = i; (int)SpaceshipState.moving; // (int)SpaceshipState.stopped; // (int)SpaceshipState.battle; // Nếu bạn khơng thích giá trị mặc định này, bạn thay đổi chúng, này: enum SpaceshipState { moving = 10, stopped = 12, battle // tự động 13 }; Vậy moving có giá trị 10, stopped có giá trị 12, battle, khơng định nghĩa rõ ràng, lấy giá trị trích xuất trước cộng thêm một, khiến 13 Lập trình C# - Phần 3: Giới thiệu Lớp Trang 17 ... đọc Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Cơ Cấu trúc (Structure) Lớp (Class) Trong ngày đầu ngơn ngữ máy tính, ngơn ngữ lập trình. .. weapons; Lập trình C# - Phần 3: Giới thiệu Lớp Trang 11 Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC }; Bạn nói với trình biên dịch tàu chiến tàu vũ trụ đặt dấu hai chấm tên lớp tên lớp. .. ngăn xếp) Lập trình C# - Phần 3: Giới thiệu Lớp Trang Câu lạc Khoa học - THPT Chuyên Lê Hồng Phong TPHCM LHPSC Lớp, khác với cấu trúc, luôn kiểu tham chiếu, tạo đống (heap), thay ngăn xếp Lớp có