Ograniczanie dostępu do właściwości

Zakończone

Klasa może określić, jak dostępny jest każdy z jego składowych, aby kodować poza klasą. Metody i zmienne, które nie mają być używane spoza klasy lub zestawu, mogą być ukryte, aby ograniczyć potencjalne błędy kodowania lub złośliwe luki w zabezpieczeniach.

Niektóre metody i właściwości mają być wywoływane lub uzyskiwane z kodu spoza klasy, znanej jako kod klienta. Inne metody i właściwości mogą być używane tylko w samej klasie. Ważne jest, aby ograniczyć dostępność kodu, aby tylko zamierzony kod klienta mógł go osiągnąć. Określasz, jak dostępne są typy i ich członkowie do kodu klienta przy użyciu następujących modyfikatorów dostępu:

  • public: typ lub element członkowski jest dostępny dla dowolnego innego kodu w tym samym zestawie lub innym zestawie, który odwołuje się do niego.
  • protected: typ lub składowa jest dostępna tylko za pomocą kodu w tej samej klasie lub klasie pochodnej.
  • internal: typ lub element członkowski jest dostępny dla dowolnego kodu w tym samym zestawie, ale nie z innego zestawu.
  • protected internal: Typ lub składowa jest dostępna przez dowolny kod w tym samym zestawie lub przez dowolną klasę pochodną w innym zestawie.
  • private: typ lub składowa jest dostępna tylko za pomocą kodu w tej samej klasie lub strukturę.
  • private protected: typ lub składowa jest dostępna tylko za pomocą kodu w tym samym zestawie i tylko przez kod w tej samej klasie lub klasie pochodnej.

Składowe klasy są domyślnie przypisywane private dostępu.

Modyfikatory dostępu do właściwości i metod dostępu

Do tego momentu w module skupiliśmy się na właściwościach, które obejmują zarówno get, jak i set akcesoriów. Te właściwości są określane jako właściwości odczytu i zapisu. Oprócz właściwości odczytu i zapisu można tworzyć właściwości tylko do odczytu lub zapewnić różne ułatwienia dostępu do set i get akcesoriów.

Domyślnie metody dostępu get i set mają ten sam poziom ułatwień dostępu co właściwość, do której należą. Można jednak ograniczyć dostępność get lub set akcesorium. Modyfikatory dostępu są przydatne, gdy chcesz ograniczyć dostęp do samej właściwości, ale nadal zezwalaj na dostęp do wartości właściwości za pośrednictwem metody dostępu get.

Załóżmy, że masz klasę Person, która powinna włączać zmianę wartości właściwości FirstName z innych metod w klasie. Można nadać set dostęp prywatny zamiast dostępu wewnętrznego lub publicznego:


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

    // Omitted for brevity.
}

Właściwość FirstName można odczytać z dowolnego kodu, ale można ją przypisać tylko z kodu w klasie Person.

Do set lub get akcesoriów można dodać dowolny restrykcyjny modyfikator dostępu. Modyfikator dostępu do poszczególnych metod dostępu musi być bardziej restrykcyjny niż dostęp do właściwości. Powyższy kod jest legalny, ponieważ właściwość FirstName jest public, ale set akcesorium jest private. Nie można zadeklarować właściwości private z akcesorem public. Deklaracje właściwości można również zadeklarować protected, internal, protected internal, a nawet private.

Istnieją dwa specjalne modyfikatory dostępu dla set akcesoriów:

  • Akcesorium set może mieć init jako modyfikator dostępu. Ta set metodę dostępu może być wywoływana tylko z inicjatora obiektu lub konstruktorów typu. Jest bardziej restrykcyjny niż private na set akcesorium.
  • Automatycznie zaimplementowana właściwość może zadeklarować metodę dostępu get bez set akcesorium. W takim przypadku kompilator umożliwia wywoływanie metody dostępu set tylko z konstruktorów typu. Jest bardziej restrykcyjny niż akcesorium init na set akcesorium.

Rozważ następujące aktualizacje klasy Person:


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

    public string FirstName { get; }

    // Omitted for brevity.
}

Powyższy przykład wymaga od wywołujących użycia konstruktora, który zawiera parametr FirstName. Obiekt wywołujący nie może użyć inicjatorów obiektów do przypisania wartości do właściwości. Aby obsługiwać inicjatory, można ustawić set akcesorium init, jak pokazano w poniższym kodzie:


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

    public string? FirstName { get; init; }

    // Omitted for brevity.
}

Modyfikatory te są często używane z modyfikatorem required w celu wymuszenia prawidłowego inicjowania.

Ograniczanie ułatwień dostępu dostępu dostępu

W niektórych przypadkach warto ograniczyć dostęp do get lub set dostępu. Zazwyczaj można ograniczyć dostępność set akcesorium, zachowując dostęp get publicznie dostępny.

Na przykład:


private string _name = "Hello";

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

W tym przykładzie właściwość o nazwie Name definiuje get i set akcesorium. Akcesorium get otrzymuje poziom ułatwień dostępu samej właściwości, public w tym przypadku, podczas gdy akcesorium set jest jawnie ograniczone przez zastosowanie modyfikatora dostępu protected do samego metody dostępu.

Ograniczenia dotyczące modyfikatorów dostępu dla metod dostępu

Używanie modyfikatorów dostępu we właściwościach lub indeksatorach podlega następującym warunkom:

  • Modyfikatory dostępu można używać tylko wtedy, gdy właściwość ma zarówno set, jak i get metod dostępu. W takim przypadku modyfikator jest dozwolony tylko na jednym z dwóch metod dostępu.
  • Poziom ułatwień dostępu w metodzie dostępu musi być bardziej restrykcyjny niż poziom ułatwień dostępu w samej właściwości lub indeksatorze.

Właściwości statyczne tylko do odczytu

W poniższym przykładzie pokazano użycie wystąpień i właściwości statycznych w klasie.

Klasa Employee ma właściwość statyczną o nazwie Counter, która śledzi liczbę utworzonych wystąpień klas. Właściwość Counter jest tylko do odczytu, co oznacza, że można uzyskać do niej dostęp spoza klasy, ale nie jest modyfikowana. Właściwość Counter jest również static, co oznacza, że uzyskujesz dostęp do wartości Counter przy użyciu klasy Employee, a nie employee1 ani employee2 wystąpień klasy.


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();
    }
}

Klasa Employee ma następujące elementy członkowskie:

  • NumberOfEmployees: publiczne pole statyczne, które śledzi łączną liczbę utworzonych wystąpień pracowników. Jest statyczny, jest współużytkowany między wszystkimi wystąpieniami klasy Employee.
  • _counter: prywatne pole statyczne używane do przypisywania unikatowej liczby do każdego wystąpienia pracownika. Jest również współużytkowany we wszystkich wystąpieniach.
  • _name: pole wystąpienia prywatnego, które przechowuje nazwę pracownika. Jest on specyficzny dla każdego wystąpienia klasy Employee.
  • Name: publiczna właściwość wystąpienia odczytu i zapisu, która zapewnia dostęp do pola _name. Metoda dostępu get zwraca wartość _name, a metoda dostępu set przypisuje nową wartość do _name.
  • Counter: publiczna właściwość statyczna tylko do odczytu, która zapewnia dostęp do pola _counter. Metoda dostępu get zwraca wartość _counter. Właściwość Counter jest skojarzona z klasą, a nie z obiektami, ponieważ jest zdefiniowana jako właściwość statyczna.

Dwa pola statyczne są inicjowane po pierwszym załadowaniu klasy do pamięci przed utworzeniem wystąpień klasy. W języku C#pola statyczne są inicjowane do ich wartości domyślnych, jeśli nie są jawnie inicjowane. W przypadku pól int wartość domyślna to 0.

Właściwości statyczne i pola należą do samej klasy, a nie do żadnego konkretnego wystąpienia klasy. Wszystkie wystąpienia klasy współdzielą te same właściwości statyczne i pola.

Konstruktor zwiększa pole statyczne NumberOfEmployees o 1 i przypisuje nową wartość do pola statycznego _counter. Takie podejście zapewnia, że każde wystąpienie Employee otrzymuje unikatową liczbę.

Prywatne metody dostępu

Rozważmy następujący kod, który implementuje właściwości odczytu i zapisu:


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
*/

W tym przykładzie klasa Person zawiera właściwości Name i Age. Właściwości są publiczne i obejmują get i set akcesoriów. Publiczne metody dostępu umożliwiają odczytywanie i zapisywanie tych właściwości przez dowolny obiekt.

Czasami należy wykluczyć jedną z metod dostępu. Możesz pominąć metodę dostępu set, aby właściwość mogła być tylko do odczytu:


public string Name
{
    get
    {
        return _name;
    }
}


Alternatywnie można uwidocznić jeden dostęp publicznie, ale udostępnić drugi prywatny lub chroniony.


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