Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Khác biệt giữa Class và Struct trong C#

Tóm tắt cốt lõi

Đặc điểmClassStruct
Type CategoryReference TypeValue Type
Memory AllocationHeap (managed heap)Stack (hoặc inline trong object chứa)
AssignmentCopy referenceCopy value (shallow copy)
NullabilityCó thể nullKhông null (trừ Nullable<T>)
InheritanceHỗ trợ đầy đủ (base class, interface)Chỉ implement interface, KHÔNG kế thừa struct/class
Destructor/FinalizerCó (~Class())Không
Default ConstructorTự động có parameterlessLuôn có implicit parameterless (init all fields to 0/null)
ConstructorCó thể định nghĩa nhiềuChỉ định nghĩa constructor CÓ tham số (C# 10+ cho phép parameterless)
Protected MembersHỗ trợKhông hỗ trợ
Virtual/OverrideHỗ trợKhông (sealed implicitly)
InterfaceImplement đượcImplement đượ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

ScenarioClassStruct
Array 1M elements1M references (8MB) + 1M objects (24MB+) = ~32MB+1M values contiguous = ~16MB (Point3D: 24 bytes)
GC PressureCao (nhiều objects)Thấp (không GC cho stack/array)
Pass by valueCopy reference (8 bytes)Copy toàn bộ struct
BoxingKhôngCó 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.