ความแตกต่างหลัก: Reference Type vs Value Type
| คุณสมบัติ | Class (Reference Type) | Struct (Value Type) |
| Memory Allocation | Heap (Managed Heap) | Stack (หรือ Inline ใน Containing Type) |
| Assignment | Copy Reference (Point to Same Object) | Copy Value (Deep Copy) |
| Null | สามารถเป็น Null ได้ | ไม่สามารถเป็น Null ได้ (Nullable Struct: T?) |
| Default Value | null | Zero-initialized (ทั้ง Struct) |
| Inheritance | รองรับ (Single Inheritance) | ไม่รองรับ (Implicitly Sealed) |
| Interfaces | Implement ได้ | Implement ได้ |
| Destructor/Finalizer | มีได้ (~Class()) | ไม่มี |
| Constructor | Parameterless + Parameterized | Parameterized เท่านั้น (C# 10+ มี Parameterless) |
| protected Modifier | ใช้ได้ | ใช้ไม่ได้ |
| Field Initializers | ได้ | ไม่ได้ (C# 10-) |
| Performance | Heap Alloc, GC Pressure, Indirection | No Heap, No GC, Cache Friendly |
ตัวอย่างพฤติกรรม Assignment
class RefClass { public int Value; }
struct ValStruct { public int Value; }
class Program
{
static void Main()
{
// Class - Reference Semantics
RefClass rc1 = new RefClass { Value = 10 };
RefClass rc2 = rc1; // Copy Reference
rc2.Value = 20;
Console.WriteLine($"rc1: {rc1.Value}, rc2: {rc2.Value}"); // 20, 20 (Same Object!)
// Struct - Value Semantics
ValStruct vs1 = new ValStruct { Value = 10 };
ValStruct vs2 = vs1; // Copy Value
vs2.Value = 20;
Console.WriteLine($"vs1: {vs1.Value}, vs2: {vs2.Value}"); // 10, 20 (Independent!)
}
}
Output
rc1: 20, rc2: 20
vs1: 10, vs2: 20
เมื่อไหร่ควรใช้ Struct
- ✅ Small Data (≤ 16 bytes / 4 Fields ประมาณ)
- ✅ Immutable Data (Coordinate, DateTime, Guid, Money)
- ✅ High Volume (Arrays, Collections - No GC Pressure)
- ✅ Performance Critical (Game, Math, Graphics)
- ✅ Value Semantics ทางตรรกะ (Equality = Value Equality)
เมื่อไหร่ควรใช้ Class
- ✅ Large Data / Complex Object
- ✅ Mutable Reference Semantics (Identity สำคัญ)
- ✅ Need Inheritance / Polymorphism
- ✅ Need Null Representation
- ✅ Reference Equality (Identity) สำคัญ
- ✅ Framework Design Guidelines: Default เป็น Class
Struct Best Practices (C# 7.2+)
// readonly struct - Immutable Guarantee
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);
// Method ไม่ Mutate State
public double DistanceFromOrigin() => Math.Sqrt(X*X + Y*Y + Z*Z);
// with-expression (C# 9+) สำหรับ "Mutation"
public Point3D WithX(double newX) => this with { X = newX };
}
// ref struct - Stack Only (Span-like)
public ref struct StackOnly
{
public Span<byte> Buffer;
// ไม่สามารถเป็น Field ของ Class, ใช้ใน Stack เท่านั้น
}
Boxing/Unboxing Cost
struct Point { public int X, Y; }
Point p = new Point { X = 1, Y = 2 };
object o = p; // Boxing: Heap Alloc + Copy
Point p2 = (Point)o; // Unboxing: Copy Back
// Generic ใช้ Struct ได้โดยไม่ Boxing
List<Point> points = new(); // No Boxing
List<object> objects = new();
objects.Add(p); // Boxing!
C# 10+ Struct Enhancements
- Parameterless Constructor:
public Struct() { }
- Field Initializers:
public int X = 5;
record struct - Value-based Equality, With-expression
readonly record struct - Immutable
// C# 10+ Record Struct
public readonly record struct Point(int X, int Y)
{
public double Distance => Math.Sqrt(X*X + Y*Y);
}
// Usage
var p1 = new Point(3, 4);
var p2 = p1 with { X = 5 }; // p2 = (5, 4)
Console.WriteLine(p1 == p2); // False (Value Equality)