Biến và kiểu dữ liệu

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#.

kiểuDữLiệu tênBiến; kiểuDữLiệu tênBiến = giáTrị;

Ví dụ

int age = 20; string name = "Phat"; double score = 8.5; bool isActive = true;

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#.

abstractasbaseboolbreak bytecasecatchcharchecked classconstcontinuedecimaldefault delegatedodoubleelseenum eventexplicitexternfalsefinally fixedfloatforforeachgoto ifimplicitinintinterface internalislocklongnamespace newnullobjectoperatorout overrideparamsprivateprotectedpublic readonlyrefreturnsbytesealed shortsizeofstackallocstaticstring structswitchthisthrowtrue trytypeofuintulongunchecked unsafeushortusingvirtualvoid volatilewhile

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.

addaliasasyncawaitby descendingdynamicequalsfromget globalgroupintojoinlet nameofnotnullonorderbypartial recordremoveselectsetunmanaged valuevarwhenwherewith yield
Nếu bắt buộc phải dùng từ khóa làm tên biến, hãy thêm tiền tố @ phía trước, ví dụ @class hoặc @event. Tuy nhiên đây là thực hành không được khuyến khích.

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ụ

// Đúng int studentAge = 18; string _connectionString = "..."; const int MaxStudents = 50; // Sai int 1age = 18; // bắt đầu bằng số int student age = 18; // có khoảng trắng int int = 18; // trùng với từ khóa
Tên biến nên có ý nghĩa và phản ánh đúng mục đích sử dụng. Tránh đặt các tên quá chung chung như x, data hoặc temp trong code thực tế vì sẽ làm mã nguồn khó hiểu và khó bảo trì.
```html

Kiểu dữ liệu trong C#

C# chia kiểu dữ liệu thành hai nhóm lớn là Value TypesReference 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ú
sbyteSystem.SByte8-bit-128 đến 127
byteSystem.Byte8-bit0 đến 255
shortSystem.Int1616-bit-32,768 đến 32,767
ushortSystem.UInt1616-bit0 đến 65,535
intSystem.Int3232-bit-2,147,483,648 đến 2,147,483,647
uintSystem.UInt3232-bit0 đến 4,294,967,295
longSystem.Int6464-bit±9.2 × 10¹⁸
ulongSystem.UInt6464-bit0 đến 18.4 × 10¹⁸
floatSystem.Single32-bit~6–7 chữ số thập phân, hậu tố f
doubleSystem.Double64-bit~15–16 chữ số thập phân
decimalSystem.Decimal128-bit~28–29 chữ số thập phân, hậu tố m
charSystem.Char16-bitMột ký tự Unicode, dùng nháy đơn ' '
boolSystem.Boolean1-bitChỉ nhận true hoặc false

Bảng Reference Types

Từ khóa Kiểu .NET Ghi chú
stringSystem.StringChuỗi ký tự Unicode, bất biến (immutable)
objectSystem.ObjectLớp gốc của mọi kiểu dữ liệu trong C#
dynamicResolve lúc runtimeKiểu được xác định tại thời điểm chạy
classUser-definedKiểu do người dùng định nghĩa
interfaceUser-definedGiao diện
delegateSystem.DelegateCon trỏ hàm
Mảng []System.ArrayTậ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.

var age = 25; // compiler hiểu là int var name = "Phat"; // compiler hiểu là string var students = new List<int>(); // compiler hiểu là List<int>

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.

dynamic data = "hello"; data = 42; // hợp lệ, có thể đổi kiểu tùy ý data = new List<int>();

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.

int number = 100; long bigNumber = number;
Không áp dụng chuyển đổi ngầm định giữa bool và số, hoặc giữa decimal và float/double.

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.

double score = 8.9; int result = (int)score;

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ố.

double number = 3.9; int result = Convert.ToInt32(number);

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()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.

string input = "123"; int number = int.Parse(input);

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.

string input = "123"; int number; bool success = int.TryParse(input, out number);

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.

double price = 12.3456; string text = price.ToString("F2");

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ớnNgầ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ểuConvert.ToXxx()
Chuỗi sang số/bool với dữ liệu đáng tinKiểu.Parse()
Chuỗi sang số/bool với dữ liệu người dùng nhậpKiểu.TryParse()
Bất kỳ kiểu nào sang chuỗi.ToString()
Kiểm tra và ép kiểu Reference Type an toànToá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.

Console.Write("Xin chào "); Console.WriteLine("C#");

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.

Console.WriteLine($"Tên: {name}, Tuổi: {age}");

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 whileTryParse() để bắt người dùng nhập đúng định dạng mới cho qua.

int age; Console.Write("Nhập tuổi: "); while (!int.TryParse(Console.ReadLine(), out age)) { Console.Write("Lỗi! Vui lòng nhập lại số nguyên: "); }

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.
Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Thông báo thành công"); Console.ResetColor();
```