Einschränken des Zugriffs auf Eigenschaften

Abgeschlossen

Eine Klasse kann angeben, wie barrierefrei jedes element ist, um Code außerhalb der Klasse zu codieren. Methoden und Variablen, die nicht außerhalb der Klasse oder Assembly verwendet werden sollen, können ausgeblendet werden, um das Potenzial für Codierungsfehler oder böswillige Exploits einzuschränken.

Einige Methoden und Eigenschaften sollen von Code außerhalb einer Klasse aufgerufen oder aufgerufen werden, die als Clientcode bezeichnet wird. Andere Methoden und Eigenschaften können nur in der Klasse selbst verwendet werden. Es ist wichtig, die Barrierefreiheit Ihres Codes einzuschränken, damit nur der beabsichtigte Clientcode ihn erreichen kann. Mithilfe der folgenden Zugriffsmodifizierer geben Sie an, wie auf Ihre Typen und ihre Member zu Clientcode zugegriffen werden soll:

  • public: Auf den Typ oder das Element kann von jedem anderen Code in derselben Assembly oder einer anderen Assembly zugegriffen werden, die darauf verweist.
  • protected: Auf den Typ oder member kann nur über Code in derselben Klasse oder einer abgeleiteten Klasse zugegriffen werden.
  • internal: Auf den Typ oder das Element kann von jedem Code in derselben Assembly, aber nicht von einer anderen Assembly aus zugegriffen werden.
  • protected internal: Auf den Typ oder das Element kann von jedem Code in derselben Assembly oder von einer abgeleiteten Klasse in einer anderen Assembly zugegriffen werden.
  • private: Auf den Typ oder das Element kann nur über Code in derselben Klasse oder Struktur zugegriffen werden.
  • private protected: Auf den Typ oder das Element kann nur über Code in derselben Assembly und nur durch Code in derselben Klasse oder einer abgeleiteten Klasse zugegriffen werden.

Klassenmember werden standardmäßig private Zugriff zugewiesen.

Zugriffsmodifizierer für Eigenschaften und Accessoren

Bis zu diesem Punkt im Modul haben wir uns auf Eigenschaften konzentriert, die sowohl get als auch set Accessoren enthalten. Diese Eigenschaften werden als Lese-/Schreibeigenschaften bezeichnet. Zusätzlich zu Lese-/Schreibeigenschaften können Sie schreibgeschützte Eigenschaften erstellen oder unterschiedliche Barrierefreiheit für die set und get Accessoren gewähren.

Standardmäßig verfügen get- und set Accessoren über die gleiche Barrierefreiheitsstufe wie die Eigenschaft, zu der sie gehören. Sie können jedoch die Barrierefreiheit des get oder set Accessors einschränken. Zugriffsmodifizierer sind nützlich, wenn Sie den Zugriff auf die Eigenschaft selbst einschränken möchten, aber dennoch den Zugriff auf den Wert der Eigenschaft über einen get Accessor zulassen.

Angenommen, Sie haben eine Person Klasse, die nur das Ändern des Werts der FirstName-Eigenschaft aus anderen Methoden in der Klasse ermöglichen soll. Sie können den set Accessor private Barrierefreiheit anstelle interner oder öffentlicher Barrierefreiheit gewähren:


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

    // Omitted for brevity.
}

Die FirstName-Eigenschaft kann aus einem beliebigen Code gelesen werden, kann jedoch nur aus Code in der Person-Klasse zugewiesen werden.

Sie können jedem restriktiven Zugriffsmodifizierer entweder den set oder get Accessoren hinzufügen. Ein Zugriffsmodifizierer für einen einzelnen Accessor muss restriktiver sein als der Zugriff auf die Eigenschaft. Der vorstehende Code ist legal, da die FirstName-Eigenschaft publicist, der set Accessor jedoch privateist. Sie konnten keine private Eigenschaft mit einem public Accessor deklarieren. Eigenschaftsdeklarationen können auch protected, internal, protected internaloder sogar privatedeklariert werden.

Es gibt zwei spezielle Zugriffsmodifizierer für set Accessoren:

  • Ein set Accessor kann init als Zugriffsmodifizierer haben. Dieser set Accessor kann nur von einem Objektinitialisierer oder den Konstruktoren des Typs aufgerufen werden. Es ist restriktiver als private für den set Accessor.
  • Eine automatisch implementierte Eigenschaft kann einen get Accessor ohne set Accessor deklarieren. In diesem Fall ermöglicht der Compiler, dass der set Accessor nur von den Konstruktoren des Typs aufgerufen werden kann. Es ist restriktiver als der init Accessor auf dem set Accessor.

Berücksichtigen Sie die folgenden Aktualisierungen der Person-Klasse:


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

    public string FirstName { get; }

    // Omitted for brevity.
}

Im vorherigen Beispiel müssen Aufrufer den Konstruktor verwenden, der den parameter FirstName enthält. Aufrufer können keine Objektinitialisierer verwenden, um der Eigenschaft einen Wert zuzuweisen. Zur Unterstützung von Initialisierern können Sie den set Accessor zu einem init Accessor machen, wie im folgenden Code gezeigt:


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

    public string? FirstName { get; init; }

    // Omitted for brevity.
}

Diese Modifizierer werden häufig mit dem required Modifizierer verwendet, um die richtige Initialisierung zu erzwingen.

Einschränken der Barrierefreiheit von Accessoren

In einigen Fällen ist es hilfreich, den Zugriff auf den get oder set Accessor einzuschränken. In der Regel beschränken Sie die Barrierefreiheit des set Accessors, während der get Accessor öffentlich zugänglich bleibt.

Zum Beispiel:


private string _name = "Hello";

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

In diesem Beispiel definiert eine Eigenschaft namens Name einen get und set Accessor. Der get Accessor erhält die Barrierefreiheitsstufe der Eigenschaft selbst, public in diesem Fall, während der set Accessor explizit eingeschränkt wird, indem der protected Zugriffsmodifizierer auf den Accessor selbst angewendet wird.

Einschränkungen für Zugriffsmodifizierer für Accessoren

Die Verwendung der Accessormodifizierer für Eigenschaften oder Indexer unterliegt den folgenden Bedingungen:

  • Sie können Accessormodifizierer nur verwenden, wenn die Eigenschaft sowohl set als auch get Accessoren besitzt. In diesem Fall ist der Modifizierer nur für einen der beiden Accessoren zulässig.
  • Die Barrierefreiheitsstufe für den Accessor muss restriktiver sein als die Barrierefreiheitsstufe für die Eigenschaft oder den Indexer selbst.

Schreibgeschützte statische Eigenschaften

Im folgenden Beispiel wird die Verwendung von Instanzen und statischen Eigenschaften in einer Klasse veranschaulicht.

Die Employee Klasse verfügt über eine statische Eigenschaft namens Counter, die die Anzahl der erstellten Klasseninstanzen nachverfolgt. Die Counter-Eigenschaft ist schreibgeschützt, d. h., sie kann von außerhalb der Klasse aufgerufen werden, aber nicht geändert werden. Die Counter-Eigenschaft ist auch static, d. h., Sie greifen auf den Counter Wert mithilfe der Employee Klasse zu, nicht auf die employee1- oder employee2 Instanzen der Klasse.


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

Die Employee Klasse weist die folgenden Member auf:

  • NumberOfEmployees: Ein öffentliches statisches Feld, das die Gesamtzahl der erstellten Mitarbeiterinstanzen nachverfolgt. Da sie statisch ist, wird sie für alle Instanzen der Employee-Klasse freigegeben.
  • _counter: Ein privates statisches Feld, das verwendet wird, um jeder Employee-Instanz eine eindeutige Nummer zuzuweisen. Sie wird auch für alle Instanzen freigegeben.
  • _name: Ein privates Instanzfeld, das den Namen des Mitarbeiters speichert. Es ist spezifisch für jede Instanz der Employee-Klasse.
  • Name: Eine öffentliche Lese-/Schreibinstanzeigenschaft, die Zugriff auf das feld _name ermöglicht. Der get Accessor gibt den Wert _namezurück, und der set Accessor weist _nameeinen neuen Wert zu.
  • Counter: Eine öffentliche schreibgeschützte statische Eigenschaft, die Zugriff auf das feld _counter ermöglicht. Der get Accessor gibt den Wert _counterzurück. Die Counter-Eigenschaft ist der Klasse und nicht den Objekten zugeordnet, da sie als statische Eigenschaft definiert ist.

Die beiden statischen Felder werden initialisiert, wenn die Klasse zum ersten Mal in den Arbeitsspeicher geladen wird, bevor Instanzen der Klasse erstellt werden. In C# werden statische Felder mit ihren Standardwerten initialisiert, wenn sie nicht explizit initialisiert werden. Bei int Feldern ist der Standardwert 0.

Statische Eigenschaften und Felder gehören zur Klasse selbst, nicht zu einer bestimmten Instanz der Klasse. Alle Instanzen der Klasse verwenden dieselben statischen Eigenschaften und Felder.

Der Konstruktor erhöht das statische feld NumberOfEmployees um 1 und weist dem statischen Feld _counter den neuen Wert zu. Mit diesem Ansatz wird sichergestellt, dass jede Employee Instanz eine eindeutige Nummer erhält.

Private Accessoren

Beachten Sie den folgenden Code, der Lese-/Schreibzugriffseigenschaften implementiert:


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

In diesem Beispiel enthält die Person-Klasse Name- und Age Eigenschaften. Die Eigenschaften sind öffentlich und enthalten sowohl get als auch set Accessoren. Öffentliche Accessoren ermöglichen es jedem Objekt, diese Eigenschaften zu lesen und zu schreiben.

Manchmal ist es, einen der Accessoren auszuschließen. Sie können den set Accessor weglassen, um die Eigenschaft schreibgeschützt zu machen:


public string Name
{
    get
    {
        return _name;
    }
}


Alternativ können Sie einen Accessor öffentlich verfügbar machen, aber den anderen privaten oder geschützten.


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