Biến là gì?
Biến (variable) là một vùng lưu trữ trong bộ nhớ RAM, được đặt tên để chương trình có thể đọc và ghi dữ liệu trong quá trình thực thi. Mỗi biến gắn với một kiểu dữ liệu xác định loại giá trị mà biến đó có thể lưu trữ.
C# là ngôn ngữ strongly typed (kiểu mạnh), nghĩa là mọi biến phải được khai báo kiểu dữ liệu rõ ràng trước khi sử dụng và không thể tự ý thay đổi kiểu sau khi đã khai báo.
Cú pháp khai báo biến
Có hai cách khai báo biến trong C#.
Ví dụ
Từ khóa trong C#
Từ khóa (keyword) là những tên đã được C# đặt sẵn với ý nghĩa cố định. Lập trình viên không được sử dụng các từ này làm tên biến, tên class hoặc các định danh khác trong chương trình.
Dưới đây là toàn bộ từ khóa trong C#.
Contextual Keywords
Ngoài các từ khóa chính, C# còn có contextual keywords. Đây là những từ chỉ có ý nghĩa đặc biệt trong một ngữ cảnh nhất định.
Quy tắc đặt tên biến
Việc đặt tên biến trong C# cần tuân theo các quy tắc của compiler và các quy ước đặt tên của Microsoft để mã nguồn dễ đọc, dễ bảo trì và thống nhất.
Quy tắc bắt buộc
- Tên biến chỉ được chứa chữ cái (a-z, A-Z), chữ số (0-9) và dấu gạch dưới (_).
- Tên biến không được bắt đầu bằng chữ số.
- Tên biến phân biệt chữ hoa và chữ thường. Ví dụ age và Age là hai biến khác nhau.
- Tên biến không được trùng với từ khóa của C#.
Quy ước đặt tên của Microsoft
C# theo chuẩn đặt tên của Microsoft như sau.
| Loại | Quy ước | Ví dụ |
|---|---|---|
| Biến cục bộ (local variable) | camelCase | studentName, totalPrice |
| Tham số (parameter) | camelCase | courseId, userName |
| Trường private (private field) | _ + camelCase | _repository, _logger |
| Thuộc tính (property) | PascalCase | StudentName, TotalPrice |
| Hằng số (const) | PascalCase | MaxCapacity, DefaultRate |
| Phương thức (method) | PascalCase | GetStudentById() |
| Class, Interface | PascalCase | StudentService, IRepository |
Ví dụ
Kiểu dữ liệu trong C#
C# chia kiểu dữ liệu thành hai nhóm lớn là Value Types và Reference Types.
Value Type lưu giá trị trực tiếp trên bộ nhớ Stack. Khi gán cho một biến khác, hệ thống sẽ tạo ra một bản sao độc lập. Vì vậy, việc thay đổi biến mới sẽ không ảnh hưởng đến biến gốc.
Reference Type lưu địa chỉ tham chiếu đến dữ liệu thực sự nằm trên Heap. Khi gán cho một biến khác, cả hai biến sẽ cùng trỏ đến một vùng nhớ. Vì vậy, thay đổi dữ liệu thông qua biến này có thể ảnh hưởng đến biến kia.
Bảng Value Types
| Từ khóa | Kiểu .NET | Kích thước | Phạm vi / Ghi chú |
|---|---|---|---|
| sbyte | System.SByte | 8-bit | -128 đến 127 |
| byte | System.Byte | 8-bit | 0 đến 255 |
| short | System.Int16 | 16-bit | -32,768 đến 32,767 |
| ushort | System.UInt16 | 16-bit | 0 đến 65,535 |
| int | System.Int32 | 32-bit | -2,147,483,648 đến 2,147,483,647 |
| uint | System.UInt32 | 32-bit | 0 đến 4,294,967,295 |
| long | System.Int64 | 64-bit | ±9.2 × 10¹⁸ |
| ulong | System.UInt64 | 64-bit | 0 đến 18.4 × 10¹⁸ |
| float | System.Single | 32-bit | ~6–7 chữ số thập phân, hậu tố f |
| double | System.Double | 64-bit | ~15–16 chữ số thập phân |
| decimal | System.Decimal | 128-bit | ~28–29 chữ số thập phân, hậu tố m |
| char | System.Char | 16-bit | Một ký tự Unicode, dùng nháy đơn ' ' |
| bool | System.Boolean | 1-bit | Chỉ nhận true hoặc false |
Bảng Reference Types
| Từ khóa | Kiểu .NET | Ghi chú |
|---|---|---|
| string | System.String | Chuỗi ký tự Unicode, bất biến (immutable) |
| object | System.Object | Lớp gốc của mọi kiểu dữ liệu trong C# |
| dynamic | Resolve lúc runtime | Kiểu được xác định tại thời điểm chạy |
| class | User-defined | Kiểu do người dùng định nghĩa |
| interface | User-defined | Giao diện |
| delegate | System.Delegate | Con trỏ hàm |
| Mảng [] | System.Array | Tập hợp phần tử cùng kiểu |
Một số kiểu dữ liệu đặc biệt
var - Suy diễn kiểu tại compile-time
var không phải là một kiểu dữ liệu riêng. Khi sử dụng var, compiler sẽ tự xác định kiểu dữ liệu dựa vào giá trị bên phải dấu = tại thời điểm biên dịch. Sau khi xác định xong, kiểu dữ liệu đó là cố định và không thể thay đổi.
Sau khi khai báo, biến age sẽ luôn là kiểu int. Nếu gán age = "hello" thì chương trình sẽ báo lỗi ngay trong quá trình biên dịch.
Nên dùng var khi kiểu dữ liệu đã rõ ràng ở phía bên phải, chẳng hạn var list = new List<Student>(). Không nên dùng var nếu làm người đọc khó xác định kiểu dữ liệu thực sự của biến, ví dụ var result = Calculate().
dynamic - Kiểu xác định tại runtime
Khác với var, dynamic bỏ qua hoàn toàn việc kiểm tra kiểu dữ liệu trong quá trình biên dịch. Kiểu thực sự và tính hợp lệ của các thao tác chỉ được kiểm tra khi chương trình đang chạy.
Vì không kiểm tra lúc compile, nếu gọi một method không tồn tại trên biến dynamic, chương trình sẽ không báo lỗi khi build mà sẽ phát sinh RuntimeBinderException khi chạy đến dòng đó.
dynamic thường được sử dụng khi làm việc với COM Interop, dữ liệu JSON không biết trước cấu trúc hoặc khi giao tiếp với các ngôn ngữ động. Trong phát triển ứng dụng thông thường, nên hạn chế sử dụng dynamic vì nó làm mất đi sự an toàn kiểu dữ liệu của C#.
Chuyển đổi kiểu dữ liệu
Chuyển đổi kiểu dữ liệu (Type Conversion) xảy ra khi gán hoặc truyền giá trị từ kiểu dữ liệu này sang kiểu dữ liệu khác trong C#.
Ngầm định
Chuyển đổi ngầm định (Implicit) là kiểu chuyển đổi tự động và an toàn. Cách này thường áp dụng khi chuyển từ kiểu dữ liệu có phạm vi nhỏ sang kiểu dữ liệu có phạm vi lớn hơn.
Tường minh
Chuyển đổi tường minh (Explicit / Casting) cần ép kiểu bằng cú pháp (kiểu)giá_trị. Cách này áp dụng khi chuyển từ kiểu lớn sang kiểu nhỏ, có nguy cơ mất dữ liệu.
Nếu ép sai kiểu hoàn toàn, chương trình có thể gây lỗi InvalidCastException. Để ép kiểu an toàn với Reference Type, có thể dùng toán tử is để kiểm tra hoặc as để trả về null nếu ép kiểu thất bại.
Lớp Convert
Lớp Convert với các phương thức Convert.ToXxx() dùng để chuyển đổi linh hoạt giữa các kiểu built-in. Khác với Casting, Convert có hỗ trợ làm tròn số.
Trong ví dụ trên, Convert.ToInt32(3.9) sẽ cho kết quả là 4. Nếu truyền sai chuỗi cần chuyển đổi, chương trình sẽ phát sinh FormatException.
Parse và TryParse
Parse() và TryParse() được dùng để chuyển chuỗi (string) sang kiểu dữ liệu tương ứng.
Parse() chuyển trực tiếp và sẽ báo lỗi nếu dữ liệu sai định dạng. Cách này phù hợp khi nguồn dữ liệu đáng tin cậy.
TryParse() chuyển đổi an toàn hơn, trả về bool để cho biết thành công hoặc thất bại và không làm chương trình văng lỗi. Cách này nên dùng khi lấy dữ liệu từ người dùng.
Phương thức ToString
Phương thức ToString() dùng để chuyển mọi kiểu dữ liệu sang chuỗi. Có thể kèm định dạng, ví dụ bienSo.ToString("F2") để lấy hai chữ số thập phân.
Bảng tóm tắt cách chuyển đổi kiểu dữ liệu
| Tình huống | Cách dùng tối ưu |
|---|---|
| Kiểu nhỏ sang kiểu lớn | Ngầm định (Implicit - Tự động) |
| Kiểu lớn sang kiểu nhỏ | Tường minh (kiểu)giá_trị |
| Chuyển đổi linh hoạt giữa các kiểu | Convert.ToXxx() |
| Chuỗi sang số/bool với dữ liệu đáng tin | Kiểu.Parse() |
| Chuỗi sang số/bool với dữ liệu người dùng nhập | Kiểu.TryParse() |
| Bất kỳ kiểu nào sang chuỗi | .ToString() |
| Kiểm tra và ép kiểu Reference Type an toàn | Toán tử is, as |
Nhập xuất dữ liệu
System.Console là lớp tĩnh có sẵn trong C#, dùng để tương tác với màn hình Console. Lớp này thường được dùng để nhập dữ liệu từ bàn phím và xuất dữ liệu ra màn hình.
Xuất dữ liệu
Console.Write() dùng để in dữ liệu ra màn hình nhưng không tự động xuống dòng.
Console.WriteLine() dùng để in dữ liệu ra màn hình và tự động xuống dòng sau khi in.
Khuyến nghị nên dùng String interpolation với ký hiệu $ để định dạng chuỗi gọn gàng và dễ đọc.
Nhập dữ liệu an toàn
Console.ReadLine() dùng để đọc dữ liệu người dùng nhập từ bàn phím và luôn trả về kiểu chuỗi (string). Nếu muốn lấy số, cần chuyển đổi kiểu dữ liệu từ string sang kiểu số tương ứng.
Kỹ thuật nhập an toàn chuẩn là kết hợp vòng lặp while và TryParse() để bắt người dùng nhập đúng định dạng mới cho qua.
Các lệnh tiện ích khác
- Console.ReadKey() dùng để đợi người dùng bấm một phím bất kỳ, thường dùng để giữ màn hình không bị tắt ngay.
- Console.Clear() dùng để xóa sạch màn hình Console.
- Console.ForegroundColor dùng để đổi màu chữ hiển thị trên màn hình Console.
- Console.ResetColor() dùng để trả màu chữ về màu mặc định sau khi đổi màu.