Hạn chế truy nhập vào thuộc tính

Hoàn thành

Một lớp học có thể chỉ định mức độ truy nhập của mỗi thành viên trong lớp đó là mã bên ngoài lớp. Có thể ẩn các phương pháp và biến không nhằm mục đích sử dụng từ bên ngoài lớp hoặc tổ hợp để giới hạn khả năng xảy ra lỗi mã hóa hoặc khai thác độc hại.

Một số phương pháp và thuộc tính có nghĩa là được gọi hoặc truy cập từ mã bên ngoài một lớp, được gọi là mã khách hàng. Các phương pháp và thuộc tính khác có thể chỉ để sử dụng trong chính lớp đó. Điều quan trọng là phải giới hạn khả năng truy nhập của mã để chỉ mã máy khách dự kiến mới có thể truy nhập được mã đó. Bạn chỉ định cách có thể truy nhập các loại của bạn và thành viên của họ đối với mã máy khách bằng cách sử dụng các bổ trợ truy nhập sau:

  • public: Bất kỳ mã nào khác trong cùng tổ hợp hoặc tổ hợp khác tham chiếu đến loại hoặc thành viên đó đều có thể truy nhập được.
  • protected: Loại hoặc thành viên chỉ có thể truy nhập bằng mã trong cùng một lớp hoặc lớp dẫn xuất.
  • internal: Loại hoặc thành viên có thể được truy nhập bởi bất kỳ mã nào trong cùng một hợp ngữ, nhưng không phải từ một hợp ngữ khác.
  • protected internal: Loại hoặc thành viên có thể truy cập bằng bất kỳ mã nào trong cùng một hợp ngữ, hoặc bởi bất kỳ lớp dẫn xuất trong một hợp ngữ khác.
  • private: Chỉ mã trong cùng lớp hoặc hướng dẫn mới có thể truy nhập loại hoặc phần tử.
  • private protected: Loại hoặc thành viên chỉ có thể truy nhập bằng mã trong cùng một hợp ngữ và chỉ bằng mã trong cùng một lớp hoặc lớp dẫn xuất.

Thành viên lớp được gán quyền private truy nhập theo mặc định.

Bổ trợ Truy nhập trên thuộc tính và phụ kiện

Đến thời điểm này trong mô-đun, chúng tôi tập trung vào các thuộc tính bao gồm cả getset truy cập. Những thuộc tính này được gọi là thuộc tính đọc-ghi. Ngoài các thuộc tính đọc-ghi, bạn có thể tạo thuộc tính chỉ đọc hoặc cung cấp khả năng truy nhập khác nhau cho setget truy nhập.

Theo mặc định, getset truy nhập có cùng mức khả năng truy nhập với thuộc tính chứa chúng. Tuy nhiên, bạn có thể hạn chế khả năng truy nhập của get hoặc set truy nhập. Bổ trợ truy nhập rất hữu ích khi bạn muốn hạn chế quyền truy nhập vào chính thuộc tính đó nhưng vẫn cho phép truy nhập vào giá trị của thuộc tính thông qua một get của bạn.

Giả sử bạn có Person lớp học mà chỉ nên cho phép thay đổi giá trị của thuộc tính FirstName từ các phương pháp khác trong lớp. Bạn có thể cấp cho người set năng riêng tư cho người truy cập thay vì nội bộ hoặc công khai:


public class Person
{
    public string? FirstName { get; private set; }

    // Omitted for brevity.
}

Thuộc FirstName có thể đọc từ bất kỳ mã nào, nhưng chỉ có thể gán thuộc tính này từ mã trong lớp Person bạn.

Bạn có thể thêm bất kỳ bổ trợ truy nhập hạn chế nào vào set hoặc get bổ trợ. Bổ trợ truy nhập trên một người truy cập cá nhân phải hạn chế hơn so với truy cập của tài sản. Mã trước là hợp pháp vì thuộc tính FirstNamepublic, nhưng người set truy nhập là private. Bạn không thể khai báo thuộc private với người truy public khác. Khai báo thuộc tính cũng có thể được khai báo protected, internal, protected internal, hoặc, thậm chí là private.

Có hai bộ điều chỉnh truy cập đặc biệt cho set phụ kiện:

  • Người set có thể sử dụng bộ điều init làm trợ phím truy nhập của mình. Điều set có thể được gọi chỉ từ bộ khởi tạo đối tượng hoặc hàm tạo của loại. Nó hạn chế hơn so với private trên set truy cập.
  • Một thuộc tính được triển khai tự động có thể khai báo get truy nhập không có người set truy nhập. Trong trường hợp đó, trình biên dịch cho phép người set được gọi chỉ từ các hàm tạo của loại. Điều này hạn chế hơn so với người init trên trang set truy nhập.

Hãy cân nhắc các cập nhật sau cho lớp Cá nhân:


public class Person
{
    public Person(string firstName) => FirstName = firstName;

    public string FirstName { get; }

    // Omitted for brevity.
}

Ví dụ trên đây yêu cầu người gọi sử dụng hàm dựng bao gồm tham FirstName số. Người gọi không thể sử dụng bộ khởi tạo đối tượng để gán giá trị cho thuộc tính. Để hỗ trợ khởi tạo, bạn có thể đặt người set làm người init nối mới, như minh họa trong mã sau đây:


public class Person
{
    public Person() { }
    public Person(string firstName) => FirstName = firstName;

    public string? FirstName { get; init; }

    // Omitted for brevity.
}

Những bổ trợ này thường được sử dụng cùng với required trợ để bắt buộc khởi tạo thích hợp.

Hạn chế trợ năng của người truy nhập

Trong một số trường hợp, sẽ hữu ích khi hạn chế quyền truy nhập đối với người get hoặc người set truy nhập. Thông thường, bạn hạn chế trợ năng của người set truy nhập, trong khi vẫn giữ cho người truy get truy nhập công khai.

Chẳng hạn:


private string _name = "Hello";

public string Name
{
    get
    {
        return _name;
    }
    protected set
    {
        _name = value;
    }
}

Trong ví dụ này, một thuộc tính Name xác định một getset truy nhập. Người truy nhập get sẽ nhận được mức trợ năng của chính thuộc tính đó, public trong trường hợp này, trong khi người truy nhập set rõ ràng bị hạn chế bằng cách áp dụng bộ điều chỉnh truy nhập protected cho chính người truy nhập đó.

Hạn chế về bổ trợ truy nhập đối với người truy cập

Việc sử dụng bộ điều chỉnh của người truy nhập trên thuộc tính hoặc bộ lập chỉ mục phải tuân theo các điều kiện sau:

  • Bạn chỉ có thể sử dụng bổ trợ cho người truy nhập nếu thuộc tính có cả setget phụ. Trong trường hợp này, bộ điều chỉnh chỉ được phép trên một trong hai phụ kiện.
  • Mức khả năng truy nhập trên người truy nhập phải hạn chế hơn mức khả năng truy nhập trên chính thuộc tính hoặc bộ chỉ mục.

Thuộc tính tĩnh chỉ đọc

Ví dụ sau đây minh họa việc sử dụng các thuộc tính tĩnh và thể hiện trong lớp.

Lớp Employee có thuộc tính tĩnh có tên là Counter theo dõi số lượng phiên bản lớp được tạo. Thuộc Counter có thuộc tính chỉ đọc, có nghĩa là có thể truy nhập từ bên ngoài lớp nhưng không được sửa đổi. Thuộc tính Counter cũng là static, nghĩa là bạn truy nhập giá trị Counter bằng cách sử dụng lớp Employee, chứ không phải phiên bản employee1 hoặc employee2 thể hiện của lớp.


public class Employee
{
    public static int NumberOfEmployees;
    private static int _counter;
    private string _name;

    // A read-write instance property:
    public string Name
    {
        get => _name;
        set => _name = value;
    }

    // A read-only static property:
    public static int Counter => _counter;

    // A Constructor:
    public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}

public class Program
{
    static void Main(string[] args)
    {
        // Create an initial instance of the Employee class
        Employee employee1 = new Employee { Name = "NameOne" };
        
        // Display the name and counter values
        Console.WriteLine($"Employee 1: Name = {employee1.Name}, Counter = {Employee.Counter}");

        // Create a second instance of the Employee class
        Employee employee2 = new Employee { Name = "NameTwo" };

        // Display the name and counter values
        Console.WriteLine($"Employee 2: Name = {employee2.Name}, Counter = {Employee.Counter}");

        employee2.Name = "NameThree";

        // Display the name and counter values
        Console.WriteLine($"Employee 2: Name = {employee2.Name}, Counter = {Employee.Counter}");

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

Lớp Employee có các thành viên sau:

  • NumberOfEmployees: Một trường tĩnh công cộng theo dõi tổng số phiên bản Nhân viên được tạo. Là tĩnh, nó được chia sẻ giữa tất cả các trường hợp của lớp Nhân viên.
  • _counter: Một trường tĩnh riêng được sử dụng để gán một số duy nhất cho mỗi phiên bản Nhân viên. Nó cũng được chia sẻ trong tất cả các trường hợp.
  • _name: Trường phiên bản riêng lưu trữ tên nhân viên. Tính năng này dành riêng cho từng trường hợp của lớp Nhân viên.
  • Name: Thuộc tính phiên bản đọc-ghi công cộng cung cấp quyền truy nhập _name trường. Người get trả về giá trị của _name, và người truy set gán một giá trị mới cho _name.
  • Counter: Thuộc tính tĩnh chỉ đọc công cộng cung cấp quyền truy nhập vào trường _counter công cộng. Người get trả về giá trị của _counter. Thuộc Counter được liên kết với lớp chứ không phải là đối tượng vì thuộc tính này được xác định là thuộc tính tĩnh.

Hai trường tĩnh được khởi tạo khi lớp đầu tiên được tải vào bộ nhớ, trước khi bất kỳ trường hợp nào của lớp được tạo ra. Trong C#, các trường tĩnh được khởi tạo thành giá trị mặc định nếu chưa được khởi tạo rõ ràng. Đối int trường, giá trị mặc định là 0.

Thuộc tính tĩnh và trường thuộc về chính lớp, không thuộc bất kỳ trường hợp cụ thể nào của lớp. Tất cả các phiên bản của lớp chia sẻ cùng một thuộc tính và trường tĩnh.

Hàm tạo tăng NumberOfEmployees trường tĩnh lên 1 và gán giá trị mới cho _counter tĩnh. Phương pháp này đảm bảo rằng mỗi Employee thể nhận được một số duy nhất.

Người truy cập cá nhân

Hãy xem xét các mã sau đây mà thực hiện đọc-ghi thuộc tính:


class Person
{
    private string _name = "N/A";
    private int _age = 0;

    // Declare a Name property of type string:
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }

    // Declare an Age property of type int:
    public int Age
    {
        get
        {
            return _age;
        }
        set
        {
            _age = value;
        }
    }
}

class TestPerson
{
    static void Main()
    {
        // Create a new Person object:
        Person person = new Person();

        // Print out the name and the age associated with the person:
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Set some values on the person object:
        person.Name = "NameOne";
        person.Age = 99;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Increment the Age property:
        person.Age += 1;
        Console.WriteLine($"Person details - Name = {person.Name}, Age = {person.Age}");

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}

/* Output:
Person details - Name = N/A, Age = 0
Person details - Name = NameOne, Age = 99
Person details - Name = NameOne, Age = 100
*/

Trong ví dụ này, lớp Person bao gồm các thuộc NameAge khác nhau. Các thuộc tính được công khai và cả hai bao gồm getset phụ. Người truy nhập công cộng cho phép bất kỳ đối tượng nào đọc và ghi các thuộc tính này.

Đôi khi, cần loại trừ một trong các người truy cập. Bạn có thể bỏ qua set truy nhập để đặt thuộc tính thành chỉ đọc:


public string Name
{
    get
    {
        return _name;
    }
}


Ngoài ra, bạn có thể hiển thị công khai một người truy cập nhưng đặt người kia ở chế độ riêng tư hoặc được bảo vệ.


public string Name
{
    get
    {
        return _name;
    }
    private set
    {
        _name = value;
    }
}