Types and Variables
Định nghĩa (Definition)
C# có một hệ thống kiểu thống nhất (Unified Type System) trong đó tất cả các kiểu đều kế thừa từ System.Object. Ngôn ngữ là kiểu mạnh (Strongly-typed): mọi biến, hằng số và biểu thức đều có kiểu được xác định tại thời điểm biên dịch, và trình biên dịch thực thi tính tương thích kiểu. C# phân biệt giữa kiểu giá trị (Value Types) (lưu trữ trực tiếp) và kiểu tham chiếu (Reference Types) (lưu trữ qua tham chiếu).
Các khái niệm cốt lõi (Core Concepts)
Kiểu giá trị (Value Types) vs Kiểu tham chiếu (Reference Types)
| Khía cạnh | Kiểu giá trị (Value Types) | Kiểu tham chiếu (Reference Types) |
|---|---|---|
| Lưu trữ trong | Stack (thường) | Heap |
| Chứa | Dữ liệu trực tiếp | Tham chiếu (Con trỏ/Pointer) đến dữ liệu |
| Gán biến | Sao chép giá trị | Sao chép tham chiếu |
| Mặc định | Không thể là null (trừ Nullable<T>) | Có thể là null |
| Ví dụ | int, double, bool, char, struct, enum | string, object, class, interface, delegate, array |
| Kiểu cơ sở | System.ValueType | System.Object |
Gán một kiểu giá trị (Value Type) sao chép dữ liệu. Gán một kiểu tham chiếu (Reference Type) sao chép con trỏ — cả hai biến đều trỏ đến cùng một đối tượng trên Heap. Sửa đổi một biến sẽ ảnh hưởng đến biến kia.
Bảng kiểu tích hợp sẵn (Built-in Types Table)
| Từ khóa C# | Kiểu .NET | Kích thước | Phạm vi / Mô tả |
|---|---|---|---|
int | System.Int32 | 4 bytes | -2,147,483,648 đến 2,147,483,647 |
long | System.Int64 | 8 bytes | Số nguyên rất lớn |
short | System.Int16 | 2 bytes | -32,768 đến 32,767 |
byte | System.Byte | 1 byte | 0 đến 255 |
float | System.Single | 4 bytes | Độ chính xác ~7 chữ số (hậu tố f) |
double | System.Double | 8 bytes | Độ chính xác ~15-16 chữ số |
decimal | System.Decimal | 16 bytes | Độ chính xác 28-29 chữ số (hậu tố m) |
bool | System.Boolean | 1 byte | true hoặc false |
char | System.Char | 2 bytes | Ký tự Unicode |
string | System.String | Biến đổi | Văn bản Unicode (kiểu tham chiếu!) |
object | System.Object | N/A | Cơ sở của mọi kiểu (kiểu tham chiếu!) |
Biến (Variables)
Khai báo và khởi tạo:
int age = 30;
string name = "Alice";
bool isActive = true;
var score = 95.5; // Compiler infers double
var productName = "Widget"; // Compiler infers string
Từ khóa var cho phép trình biên dịch suy luận kiểu từ biểu thức khởi tạo. Nó không phải là kiểu biến đổi (Variant) hay kiểu động (Dynamic) — biến vẫn có kiểu mạnh (Strongly Typed) tại thời điểm biên dịch.
Quy ước đặt tên:
| Phong cách | Sử dụng cho | Ví dụ |
|---|---|---|
camelCase | Biến cục bộ (Local Variables), tham số (Parameters), trường riêng (Private Fields) | orderCount, firstName |
PascalCase | Lớp (Classes), phương thức (Methods), thuộc tính (Properties), trường công khai (Public Fields) | OrderService, TotalPrice |
_camelCase | Trường riêng (Private Fields) - quy ước thay thế | _orderCount, _repository |
IPascalCase | Giao diện (Interfaces) | IDisposable, IRepository |
Hằng số (Constants): const vs readonly vs static readonly
| Từ khóa | Khi thiết lập | Ở đâu | Ghi chú |
|---|---|---|---|
const | Thời điểm biên dịch (Compile Time) | Trường (Fields) hoặc biến cục bộ (Locals) | Giá trị được nhúng trong mã gọi; chỉ hỗ trợ kiểu nguyên thủy |
readonly | Thời gian chạy (Runtime - trong constructor) | Trường thực thể (Instance Fields) | Giá trị theo từng thực thể, thiết lập trong constructor |
static readonly | Thời gian chạy (Runtime - trong static constructor hoặc initializer) | Trường tĩnh (Static Fields) | Chia sẻ giữa mọi thực thể; có thể chứa đối tượng phức tạp |
public class Configuration
{
public const int MaxRetries = 3; // Compile-time constant
public readonly string _instanceName; // Set in constructor
public static readonly DateTime LaunchDate // Set once at runtime
= DateTime.UtcNow;
}
Giá trị const được nhúng inline tại thời điểm biên dịch. Nếu bạn thay đổi một const trong thư viện, mã gọi được biên dịch với giá trị cũ sẽ vẫn sử dụng giá trị cũ cho đến khi biên dịch lại. Sử dụng static readonly cho các giá trị có thể thay đổi giữa các phiên bản.
Toán tử (Operators)
Số học (Arithmetic): +, -, *, /, %, ++, --
So sánh (Comparison): ==, !=, <, >, <=, >=
Logic (Logical): &&, ||, !
Toán tử xử lý null (Null-handling Operators):
// Null-coalescing — trả về vế trái nếu khác null, ngược lại trả về vế phải
string name = input ?? "default";
// Null-conditional — trả về null nếu toán hạng là null (thay vì ném ngoại lệ)
int? length = name?.Length;
// Null-coalescing assignment — chỉ gán nếu biến là null
name ??= "unnamed";
Ép kiểu (Type Casting)
| Cách tiếp cận | Mô tả | Ví dụ |
|---|---|---|
| Ẩn (Implicit) | An toàn, không mất dữ liệu; tự động bởi trình biên dịch | int → long, float → double |
| Rõ ràng (Explicit cast) | Có thể mất dữ liệu; yêu cầu cú pháp (Type) | (int)3.14 → 3 |
Lớp Convert | Chuyển đổi giữa các kiểu cơ sở | Convert.ToInt32("42") |
Parse | Phân tích chuỗi; ném ngoại lệ khi thất bại | int.Parse("42") |
TryParse | Phân tích chuỗi; trả về bool | int.TryParse("42", out int result) |
// Implicit — chuyển đổi mở rộng an toàn
int x = 100;
long big = x; // int → long, no cast needed
// Explicit — thu hẹp, có thể mất dữ liệu
double pi = 3.14159;
int truncated = (int)pi; // 3
// TryParse — phân tích an toàn
if (int.TryParse(userInput, out int result))
{
Console.WriteLine($"Parsed: {result}");
}
Boxing và Unboxing
Boxing là chuyển đổi một kiểu giá trị (Value Type) thành kiểu tham chiếu (Reference Type) (object hoặc interface). Giá trị được bọc trong một đối tượng trên Heap.
Unboxing là trích xuất kiểu giá trị (Value Type) từ đối tượng đã boxing bằng ép kiểu rõ ràng (Explicit Cast).
int number = 42;
object boxed = number; // Boxing — value copied to heap
int unboxed = (int)boxed; // Unboxing — value copied back to stack
Boxing cấp phát bộ nhớ trên Heap và gây áp lực GC. Tránh sử dụng trong các đường dẫn nóng (Hot Paths). Sử dụng generics (List<int> thay vì ArrayList) và tránh tham số object khi có thể.
Ví dụ mã (Code Examples)
Hành vi kiểu giá trị vs kiểu tham chiếu (Value vs Reference Type Behavior)
// Value type — assignment copies the value
int a = 10;
int b = a;
b = 20;
Console.WriteLine(a); // 10 — unchanged
// Reference type — assignment copies the reference
var list1 = new List<int> { 1, 2, 3 };
var list2 = list1;
list2.Add(4);
Console.WriteLine(list1.Count); // 4 — affected!
Kiểu giá trị tùy chỉnh (Custom Value Type - struct)
public struct Point
{
public double X { get; }
public double Y { get; }
public Point(double x, double y) => (X, Y) = (x, y);
public double DistanceTo(Point other) =>
Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
}
Phân tích an toàn với TryParse (Safe Parsing with TryParse)
public int? ParseAge(string input)
{
if (int.TryParse(input, out int age) && age is >= 0 and <= 150)
{
return age;
}
return null;
}
Khi nào sử dụng (When to Use)
| Tình huống | Khuyến nghị |
|---|---|
| Tính toán tài chính (Financial Calculations) | Sử dụng decimal — tránh lỗi làm tròn số dấu phẩy động |
| Khoa học / đồ họa | Sử dụng double — hiệu năng và độ chính xác đủ dùng |
| Phân tích đầu vào người dùng (User Input) | Luôn ưu tiên TryParse hơn Parse |
| Giá trị cấu hình bất biến (Immutable Configuration) | Sử dụng const cho hằng số thực sự; static readonly cho các trường hợp khác |
| Cấu trúc dữ liệu nhỏ (16 bytes trở xuống) | Cân nhắc struct để cấp phát trên Stack và không có overhead GC |
Lỗi thường gặp (Common Pitfalls)
- Struct có thể thay đổi (Mutable Structs) — struct là kiểu giá trị; sửa đổi thuộc tính thông qua phương thức trả về struct sẽ sửa đổi một bản sao. Hãy làm cho struct bất biến (Immutable).
==vsEqualscho chuỗi (Strings) —==so sánh giá trị cho chuỗi (toán tử đã nạp chồng), nhưng cho các kiểu tham chiếu khác nó so sánh tham chiếu. Sử dụngstring.Equals(a, b, StringComparison.Ordinal)để so sánh rõ ràng, an toàn về culture.decimalvsdoublecho tiền tệ —doublecó lỗi làm tròn (0.1 + 0.2 != 0.3). Luôn sử dụngdecimalcho giá trị tài chính.varvà khả năng đọc — sử dụngvarkhi kiểu rõ ràng từ vế phải (var stream = new FileStream(...)). Tránh khi kiểu không rõ ràng (var result = GetData()).- Boxing trong tập hợp không generic (Non-Generic Collections) —
ArrayList,Hashtablelưu trữobject, gây boxing cho mọi kiểu giá trị. S ử dụngList<T>,Dictionary<TKey, TValue>.
Tóm tắt要点 (Key Takeaways)
- C# có hệ thống kiểu thống nhất (Unified Type System) — mọi thứ đều kế thừa từ
System.Object. - Kiểu giá trị (Value Types) nằm trên Stack (thường); kiểu tham chiếu (Reference Types) nằm trên Heap với tham chiếu trên Stack.
- Ngữ nghĩa gán khác nhau: kiểu giá trị sao chép dữ liệu; kiểu tham chiếu sao chép tham chiếu.
- Sử dụng
decimalcho tiền tệ,TryParsecho đầu vào người dùng và generics để tránh boxing. constlà thời điểm biên dịch (Compile Time);readonlylà thời gian chạy (Runtime) — chọn dựa trên nhu cầu phiên bản (Versioning).
Câu hỏi phỏng vấn (Interview Questions)
Q: Boxing là gì?
Boxing là quá trình chuyển đổi một kiểu giá trị (Value Type) thành kiểu tham chiếu (Reference Type) bằng cách bọc nó trong
System.Objecttrên Heap. Điều này gây cấp phát Heap và overhead GC. Unboxing trích xuất giá trị bằng ép kiểu rõ ràng (Explicit Cast). Sử dụng generics để tránh boxing.
Q: Sự khác biệt giữa kiểu giá trị (Value Types) và kiểu tham chiếu (Reference Types) là gì?
Kiểu giá trị (Value Types) lưu trữ dữ liệu trực tiếp (thường trên Stack) và được sao chép khi gán. Kiểu tham chiếu (Reference Types) lưu trữ con trỏ đến dữ liệu trên Heap — phép gán sao chép tham chiếu, không phải dữ liệu. Kiểu giá trị kế thừa từ
System.ValueType; kiểu tham chiếu kế thừa trực tiếp từSystem.Object.
Q: Sự khác biệt giữa const và readonly là gì?
Giá trị
constđược thiết lập tại thời điểm biên dịch (Compile Time) và nhúng vào mã gọi — chúng không thể chứa đối tượng phức tạp và thay đổi yêu cầu biên dịch lại mã gọi. Giá trịreadonlyđược thiết lập trong thời gian chạy (Runtime - trong constructor) và có thể chứa bất kỳ kiểu nào. Sử dụngstatic readonlycho hằng số runtime dùng chung.
Q: Từ khóa var là gì?
varcho phép nhập kiểu ẩn (Implicit Typing) — trình biên dịch suy luận kiểu thực tế từ biểu thức khởi tạo. Biến vẫn có kiểu mạnh (Strongly Typed) tại thời điểm biên dịch;varkhông phải kiểu động (Dynamic). Nó cải thiện khả năng đọc khi kiểu rõ ràng nhưng có thể làm giảm tính rõ ràng khi vế phải mơ hồ.