Sealed Classes
Định nghĩa (Definition)
Sealed class (Lớp bị phong ấn) là một class không thể bị kế thừa. Đánh dấu một class với từ khóa sealed ngăn chặn việc dẫn xuất và cho phép trình biên dịch JIT tối ưu hóa các lệnh gọi phương thức, kiểm tra kiểu và thao tác mảng.
public sealed class Husky : Animal
{
public override void DoNothing() { }
public override int GetAge() => 11;
}
Xem OOP để biết cú pháp và cách sử dụng cơ bản của sealed class. Trang này tập trung vào tác động hiệu suất (Performance Implications).
Tại sao Sealed Class nhanh hơn
1. Hủy ảo hóa (Devirtualization) — Lệnh gọi trực tiếp
Khi gọi một virtual method trên một open class, runtime phải sử dụng phân phối ảo (Virtual Dispatch) — nó tra cứu bảng phương thức (Method Table) để tìm override chính xác. Việc này liên quan đến các lệnh mov thêm trong assembly được JIT biên dịch.
Với một sealed class, JIT biết rằng không có override tiếp theo. Nó có thể thay thế lệnh gọi gián tiếp bằng một lệnh gọi trực tiếp (Direct Call) đến địa chỉ bộ nhớ của phương thức — hủy ảo hóa (Devirtualization).
Benchmark (BenchmarkDotNet):
| Phương thức | Trung bình | Ghi chú |
|---|---|---|
Sealed_VoidMethod | 0.0030 ns | Lệnh gọi trực tiếp — gần như miễn phí |
Open_VoidMethod | 0.6350 ns | Overhead của phân phối ảo |
// JIT thấy sealed → hủy ảo hóa → lệnh gọi trực tiếp
[Benchmark]
public void Sealed_VoidMethod() => _husky.DoNothing();
// JIT thấy open class → phải sử dụng phân phối ảo
[Benchmark]
public void Open_VoidMethod() => _bear.DoNothing();
Mẫu tương tự cũng đúng cho các phương thức có giá trị trả về:
| Phương thức | Trung bình |
|---|---|
Sealed_IntMethod | 0.0857 ns |
Open_IntMethod | 0.5081 ns |
Nếu JIT có thể xác định chính xác kiểu tại thời điểm biên dịch (ví dụ: object được tạo và sử dụng trong cùng một phương thức), nó sẽ hủy ảo hóa ngay cả open class. Khoảng cách hiệu suất biến mất trong trường hợp đó. Sealed class đảm bảo tối ưu hóa này bất kể ngữ cảnh.
2. Kiểm tra kiểu và ép kiểu nhanh hơn (Faster Type Checking and Casting)
Các toán tử is và as nhanh hơn đáng kể trên các kiểu sealed vì runtime chỉ cần so sánh với một kiểu cụ thể duy nhất. Đối với open class, nó phải duyệt toàn bộ hệ thống phân cấp kiểu (Type Hierarchy).
Kiểm tra kiểu (is):
| Phương thức | Trung bình |
|---|---|
Sealed_TypeCheck | 0.3880 ns |
Open_TypeCheck | 2.0330 ns |
Ép kiểu (as):
| Phương thức | Trung bình |
|---|---|
Sealed_Casting | 0.2757 ns |
Open_Casting | 2.0827 ns |
private readonly Animal _animal = new Animal();
// Sealed: so sánh một kiểu duy nhất
[Benchmark]
public Husky? Sealed_Casting() => _animal as Husky;
// Open: phải kiểm tra toàn bộ hệ thống phân cấp
[Benchmark]
public Bear? Open_Casting() => _animal as Bear;
3. Loại bỏ kiểm tra hiệp biến mảng (Array Covariance Check Elimination)
Lưu trữ phần tử trong mảng kiểu tham chiếu yêu cầu kiểm tra hiệp biến (Covariance Check) — runtime phải xác minh kiểu phần tử có thể gán được. Sealed class bỏ qua kiểm tra này vì không có kiểu con (Subtype).
| Phương thức | Trung bình |
|---|---|
Sealed_AddToArray | 4.322 ns |
Open_AddToArray | 5.629 ns |
4. Lệnh gọi phương thức tĩnh (Static Method Calls) — Không khác biệt
Static method không thể bị ghi đè, nên không có phân phối ảo bất kể class có sealed hay không:
| Phương thức | Trung bình |
|---|---|
Sealed_StaticMethod | 0.0183 ns |
Open_StaticMethod | 0.0340 ns |
5. ToString() — Cải thiện nhỏ
| Phương thức | Trung bình |
|---|---|
Sealed_ToString | 5.731 ns |
Open_ToString | 7.149 ns |
Phát hiện code không thể truy cập (Unreachable Code Detection)
Trình biên dịch có thể phát hiện các ép kiểu và kiểm tra kiểu không thể xảy ra trên sealed class tại thời điểm biên dịch:
public interface IFlyingAnimal { }
// Lỗi biên dịch: Husky là sealed và không implement IFlyingAnimal
var flying = _animal as Husky; // trình biên dịch biết điều này không bao giờ có thể là IFlyingAnimal
// Cảnh báo: điều kiện luôn false
if (_animal is Husky && _animal is IFlyingAnimal) { } // không thể truy cập
Điều này giúp giảm code chết (Dead Code) và phát hiện lỗi logic sớm.
Đánh đổi (Trade-offs)
Khó khăn khi Mock (Mocking Difficulty)
Sealed class không thể bị mock bởi hầu hết các mocking framework (ví dụ: Moq):
// System.NotSupportedException — Kiểu cần mock phải là interface,
// abstract class hoặc non-sealed class
var mock = new Mock<Husky>();
Cách giải quyết: Lập trình theo interface thay vì kiểu sealed cụ thể, sau đó mock interface đó.
Khi nào nên Seal
| Tình huống | Có Seal? |
|---|---|
| Class không có lý do hợp lý để bị kế thừa | Có |
| Đường dẫn code nhạy cảm hiệu suất (Hot Paths) | Có |
| Class quan trọng bảo mật không được phép mở rộng | Có |
| Class cần mock trong unit test | Cân nhắc trừu tượng hóa interface trước |
| Class là một phần của public API được thiết kế để mở rộng | Không |