Tóm tắt cốt lõi
| Đặc điểm | Class | Struct |
|---|---|---|
| Type Category | Reference Type | Value Type |
| Memory Allocation | Heap (managed heap) | Stack (hoặc inline trong object chứa) |
| Assignment | Copy reference | Copy value (shallow copy) |
| Nullability | Có thể null | Không null (trừ Nullable<T>) |
| Inheritance | Hỗ trợ đầy đủ (base class, interface) | Chỉ implement interface, KHÔNG kế thừa struct/class |
| Destructor/Finalizer | Có (~Class()) | Không |
| Default Constructor | Tự động có parameterless | Luôn có implicit parameterless (init all fields to 0/null) |
| Constructor | Có thể định nghĩa nhiều | Chỉ định nghĩa constructor CÓ tham số (C# 10+ cho phép parameterless) |
| Protected Members | Hỗ trợ | Không hỗ trợ |
| Virtual/Override | Hỗ trợ | Không (sealed implicitly) |
| Interface | Implement được | Implement được (boxing khi cast to interface) |
Memory Model Visualization
// CLASS - Reference Type
Class A { public int X; }
A a1 = new A { X = 10 };
A a2 = a1; // Copy reference
a2.X = 20;
Console.WriteLine(a1.X); // 20 - cùng object!
// STRUCT - Value Type
struct B { public int X; }
B b1 = new B { X = 10 };
B b2 = b1; // Copy VALUE (shallow copy)
b2.X = 20;
Console.WriteLine(b1.X); // 10 - độc lập!
Ví dụ minh họa hành vi
public class PointClass
{
public int X, Y;
public PointClass(int x, int y) { X = x; Y = y; }
}
public struct PointStruct
{
public int X, Y;
public PointStruct(int x, int y) { X = x; Y = y; }
}
class Program
{
static void ModifyClass(PointClass p) { p.X = 99; }
static void ModifyStruct(PointStruct p) { p.X = 99; } // Modifies copy!
static void Main()
{
var pc = new PointClass(1, 2);
var ps = new PointStruct(1, 2);
ModifyClass(pc);
ModifyStruct(ps);
Console.WriteLine($"Class: {pc.X}"); // 99 - changed!
Console.WriteLine($"Struct: {ps.X}"); // 1 - unchanged!
}
}
Khi nào dùng Class?
- Đối tượng có identity (identity matters): Entity, Service, Controller, ViewModel.
- Cần inheritance (base class, polymorphism).
- Cần reference semantics (shared state, mutable object passed around).
- Kích thước lớn (> 16-24 bytes) - tránh copy cost.
- Cần
lock,Monitor, event, delegate references. - Nullable semantics tự nhiên.
Khi nào dùng Struct?
- Kiểu dữ liệu giá trị đơn giản: Point, Vector, DateTime, Guid, Color, Money.
- Kích thước nhỏ (≤ 16 bytes ideal, ≤ 64 bytes acceptable).
- Immutable (readonly struct) - an toàn cho multi-thread.
- Performance critical: tránh GC pressure, cache-friendly (array of structs).
- Primitive-like semantics: copy by value, equality by value.
- High-frequency allocation (game dev, math, graphics).
Modern C#: readonly struct, record struct (C# 10+)
// Immutable struct - best practice cho value types
public readonly struct Point3D
{
public double X { get; init; }
public double Y { get; init; }
public double Z { get; init; }
public Point3D(double x, double y, double z) => (X, Y, Z) = (x, y, z);
public double DistanceTo(Point3D other)
=> Math.Sqrt((X-other.X)*(X-other.X) + (Y-other.Y)*(Y-other.Y) + (Z-other.Z)*(Z-other.Z));
}
// Record struct - value-based equality, with-expression, tostring
public readonly record struct Money(decimal Amount, string Currency)
{
public static Money operator +(Money a, Money b)
{
if (a.Currency != b.Currency) throw new InvalidOperationException("Currency mismatch");
return new Money(a.Amount + b.Amount, a.Currency);
}
}
// Sử dụng
var p1 = new Money(100, "USD");
var p2 = new Money(50, "USD");
var p3 = p1 + p2; // Money { Amount = 150, Currency = "USD" }
var p4 = p1 with { Amount = 200 }; // Copy with change
Performance: Class vs Struct
| Scenario | Class | Struct |
|---|---|---|
| Array 1M elements | 1M references (8MB) + 1M objects (24MB+) = ~32MB+ | 1M values contiguous = ~16MB (Point3D: 24 bytes) |
| GC Pressure | Cao (nhiều objects) | Thấp (không GC cho stack/array) |
| Pass by value | Copy reference (8 bytes) | Copy toàn bộ struct |
| Boxing | Không | Có khi cast to interface/object |
Decision Flowchart
Cần inheritance/polymorphism? → CLASS
│
└── Không → Kích thước > 64 bytes? → CLASS
│
└── Không → Immutable? → RECORD STRUCT / READONLY STRUCT
│
└── Không → Mutable, small, value semantics? → STRUCT
│
└── Không chắc → CLASS (an toàn hơn)
Common Pitfalls
⚠️ Mutable Struct là anti-pattern! Struct mutable gây bug khó debug do copy semantics. Luôn dùng
readonly struct hoặc record struct.
⚠️ Boxing Penalty: Cast struct → interface/object gây boxing allocation. Tránh trong hot path.
⚠️ Defensive Copy: Compiler tự động copy struct khi gọi method trên readonly field/property → performance hit. Dùng
readonly struct để compiler tối ưu.