Trong ngôn ngữ lập trình C#, Access Modifiers (từ khóa chỉ định truy cập) đóng vai trò quan trọng trong việc thực hiện tính đóng gói (encapsulation). Chúng xác định mức độ hiển thị của các lớp (class) và các thành phần bên trong lớp đối với các thành phần khác trong cùng một chương trình hoặc từ các chương trình bên ngoài.
Phạm vi truy cập cấp độ Class
Đối với một lớp, thông thường chúng ta sử dụng hai mức độ truy cập cơ bản:
- public: Lớp có thể được truy cập từ bất kỳ đâu, kể cả từ các assembly (Project/DLL) khác.
- internal: Lớp chỉ có thể được truy cập bên trong cùng một assembly. Đây là cấp độ mặc định nếu bạn không khai báo từ khóa cụ thể cho class.
Ví dụ về Assembly và truy cập Class
Giả sử chúng ta có một Project (Assembly) tên là DataCore. Trong đó định nghĩa các lớp như sau:
namespace DataCore
{
// Có thể truy cập từ Project khác
public class ConnectionManager
{
private QueryOptimizer optimizer = new QueryOptimizer();
}
// Chỉ có thể truy cập trong nội bộ Project DataCore
class QueryOptimizer
{
public void Optimize() { }
}
}
Khi một Project khác (ví dụ: WebApp) tham chiếu đến DataCore, nó chỉ có thể khởi tạo lớp ConnectionManager. Lớp QueryOptimizer sẽ bị ẩn hoàn toàn đối với WebApp, đảm bảo các chi tiết xử lý nội bộ không bị lộ ra ngoài.
Phạm vi truy cập cấp độ thành viên (Member Access Modifiers)
C# cung cấp các từ khóa chính để kiểm soát dữ liệu và phương thức bên trong một lớp:
- public: Không giới hạn, truy cập được từ mọi nơi.
- internal: Truy cập được bởi bất kỳ code nào trong cùng một assembly.
- protected: Chỉ truy cập được trong nội bộ lớp đó hoặc các lớp con kế thừa từ nó.
- private: Chỉ truy cập được duy nhất bên trong lớp định nghĩa nó.
- protected internal: Truy cập được từ cùng một assembly HOẶC từ lớp con ở assembly khác.
Sử dụng trong cùng một Assembly
Trong cùng một project, sự khác biệt giữa public và internal thường không rõ rệt vì mọi thứ đều có thể nhìn thấy nhau.
namespace CoreLibrary
{
public class BaseService
{
public string ServiceName;
internal string LicenseKey;
private int _instanceId;
public void DisplayInfo()
{
// Truy cập được tất cả thành phần nội bộ
Console.WriteLine($"{ServiceName} - ID: {_instanceId}");
}
}
class HelperTool
{
public void CheckService()
{
BaseService service = new BaseService();
service.ServiceName = "AuthService"; // Hợp lệ (public)
service.LicenseKey = "ABC-123"; // Hợp lệ (internal trong cùng assembly)
// service._instanceId = 1; // Lỗi: private
}
}
}
Sử dụng với quan hệ kế thừa và Assembly khác
Khi làm việc với các lớp kế thừa, đặc biệt là khi lớp con nằm ở một project khác, từ khóa protected bắt đầu phát huy tác dụng.
// Định nghĩa trong Assembly A
namespace Provider
{
public class HardwareBase
{
protected string ModelCode { get; set; }
public int Voltage { get; set; }
decimal ManufacturingCost; // Mặc định là private
}
}
// Định nghĩa trong Assembly B (có tham chiếu đến Assembly A)
namespace Consumer
{
using Provider;
public class Laptop : HardwareBase
{
public void Initialize()
{
this.ModelCode = "XPS-13"; // Hợp lệ: Truy cập được vì là lớp con (protected)
this.Voltage = 220; // Hợp lệ: public
// this.ManufacturingCost = 500; // Lỗi: private
}
}
public class Store
{
public void Inspect()
{
HardwareBase device = new HardwareBase();
device.Voltage = 110; // Hợp lệ
// device.ModelCode = "G5"; // Lỗi: Store không kế thừa HardwareBase
}
}
}
Bảng tổng kết khả năng truy cập
| Modifier | Cùng lớp | Lớp con (Cùng Assembly) | Lớp khác (Cùng Assembly) | Lớp con (Khác Assembly) | Khác Assembly |
|---|---|---|---|---|---|
| public | Có | Có | Có | Có | Có |
| internal | Có | Có | Có | Không | Không |
| protected | Có | Có | Không | Có | Không |
| private | Có | Không | Không | Không | Không |