Używanie właściwości (Przewodnik programowania w języku C#)

Właściwości łączą aspekty pól i metod. Dla użytkownika obiektu właściwość wydaje się być polem; uzyskiwanie dostępu do właściwości wymaga tej samej składni. Aby zaimplementować klasę, właściwość jest jednym lub dwoma blokami kodu reprezentującymi metodę get dostępu i/lub setinit metodę dostępu. Blok kodu dla get metody dostępu jest wykonywany, gdy właściwość jest odczytywana. Blok kodu dla set metody lub init jest wykonywany, gdy właściwość ma przypisaną wartość. Właściwość bez set metody dostępu jest uważana za tylko do odczytu. Właściwość bez get metody dostępu jest uważana za tylko do zapisu. Właściwość, która ma oba metody dostępu, to odczyt i zapis. Można użyć init metody dostępu zamiast set metody dostępu, aby umożliwić ustawienie właściwości w ramach inicjowania obiektu, ale w przeciwnym razie uczynić ją tylko do odczytu.

W przeciwieństwie do pól właściwości nie są klasyfikowane jako zmienne. W związku z tym nie można przekazać właściwości jako parametru ref lub out .

Właściwości mają wiele zastosowań:

  • Mogą weryfikować dane przed zezwoleniem na zmianę.
  • Mogą one w sposób niewidoczny uwidaczniać dane w klasie, w której te dane są pobierane z innego źródła, takiego jak baza danych.
  • Mogą podjąć akcję, gdy dane zostaną zmienione, takie jak podniesienie zdarzenia lub zmiana wartości innych pól.

Właściwości są deklarowane w bloku klasy, określając poziom dostępu pola, a następnie typ właściwości, a następnie nazwę właściwości, a następnie blok kodu, który deklaruje get-accessor i/lub set akcesorium. Na przykład:

public class Date
{
    private int _month = 7;  // Backing store

    public int Month
    {
        get => _month;
        set
        {
            if ((value > 0) && (value < 13))
            {
                _month = value;
            }
        }
    }
}

W tym przykładzie parametr jest zadeklarowany jako właściwość, Month dzięki czemu set akcesorium może upewnić się, że Month wartość jest ustawiona z zakresu od 1 do 12. Właściwość Month używa pola prywatnego do śledzenia rzeczywistej wartości. Rzeczywista lokalizacja danych właściwości jest często określana jako "magazyn zapasowy" właściwości. Właściwości często używają pól prywatnych jako magazynu zapasowego. Pole jest oznaczone jako prywatne, aby upewnić się, że można je zmienić tylko przez wywołanie właściwości . Aby uzyskać więcej informacji na temat ograniczeń dostępu publicznego i prywatnego, zobacz Modyfikatory dostępu. Właściwości zaimplementowane automatycznie zapewniają uproszczoną składnię dla prostych deklaracji właściwości. Aby uzyskać więcej informacji, zobacz Właściwości zaimplementowane automatycznie.

Metodę pobierania

Treść get metody dostępu przypomina metodę . Musi zwrócić wartość typu właściwości. Kompilator języka C# i kompilator just in time (JIT) wykrywają typowe wzorce implementowania get metody dostępu i optymalizują te wzorce. Na przykład metoda dostępu zwracająca get pole bez wykonywania żadnych obliczeń jest prawdopodobnie zoptymalizowana pod kątem odczytu pamięci tego pola. Właściwości zaimplementowane automatycznie są zgodne z tym wzorcem i korzystają z tych optymalizacji. Nie można jednak utworzyć wbudowanej metody dostępu wirtualnego get , ponieważ kompilator nie wie w czasie kompilacji, która metoda może być rzeczywiście wywoływana w czasie wykonywania. W poniższym przykładzie przedstawiono metodę get dostępu zwracającą wartość pola _nameprywatnego:

class Employee
{
    private string _name;  // the name field
    public string Name => _name;     // the Name property
}

W przypadku odwołowania się do właściwości, z wyjątkiem elementu docelowego przypisania, wywoływana jest metoda dostępu w get celu odczytania wartości właściwości. Na przykład:

var employee= new Employee();
//...

System.Console.Write(employee.Name);  // the get accessor is invoked here

Akcesorium get musi być elementem członkowskim wyrażeń lub zakończyć w instrukcji return lub throw , a kontrolka nie może przepływać poza treść akcesorium.

Ostrzeżenie

Jest to zły styl programowania, aby zmienić stan obiektu przy użyciu get metody dostępu.

Metodę get dostępu można użyć do zwrócenia wartości pola lub obliczenia i zwrócenia jej. Na przykład:

class Manager
{
    private string _name;
    public string Name => _name != null ? _name : "NA";
}

W poprzednim przykładzie, jeśli nie przypiszesz wartości do Name właściwości, zwraca wartość NA.

Akcesorium zestawu

Metoda set dostępu przypomina metodę, której typ zwracany jest void. Używa niejawnego parametru o nazwie value, którego typem jest typ właściwości. Kompilator i kompilator JIT rozpoznają również typowe wzorce dla elementu set lub init metody dostępu. Te typowe wzorce są zoptymalizowane, bezpośrednio zapisując pamięć dla pola zapasowego. W poniższym przykładzie set metoda dostępu jest dodawana do Name właściwości :

class Student
{
    private string _name;  // the name field
    public string Name    // the Name property
    {
        get => _name;
        set => _name = value;
    }
}

Po przypisaniu wartości do właściwości set metodę dostępu jest wywoływana przy użyciu argumentu, który dostarcza nową wartość. Na przykład:

var student = new Student();
student.Name = "Joe";  // the set accessor is invoked here

System.Console.Write(student.Name);  // the get accessor is invoked here

Jest to błąd podczas używania niejawnej nazwy parametru , valuedla deklaracji zmiennej lokalnej w metodzie set dostępu.

Akcesor init

Kod do utworzenia init metody dostępu jest taki sam jak kod w celu utworzenia metody dostępu, z tą różnicą set , że używasz init słowa kluczowego zamiast set. Różnica polega na tym, że init akcesorium może być używane tylko w konstruktorze lub za pomocą inicjatora obiektów.

Uwagi

Właściwości można oznaczyć jako public, , private, protectedinternal, , protected internallub private protected. Te modyfikatory dostępu definiują sposób, w jaki użytkownicy klasy mogą uzyskiwać dostęp do właściwości. Metody get i set dla tej samej właściwości mogą mieć różne modyfikatory dostępu. Na przykład get może to być public zezwolenie na dostęp tylko do odczytu spoza typu, a set może to być private lub protected. Aby uzyskać więcej informacji, zobacz Modyfikatory dostępu.

Właściwość można zadeklarować jako właściwość statyczną przy użyciu słowa kluczowego static . Właściwości statyczne są dostępne dla obiektów wywołujących w dowolnym momencie, nawet jeśli żadne wystąpienie klasy nie istnieje. Aby uzyskać więcej informacji, zobacz Klasy statyczne i składowe klas statycznych.

Właściwość można oznaczyć jako właściwość wirtualną za pomocą słowa kluczowego wirtualnego. Właściwości wirtualne umożliwiają klasom pochodnym zastąpienie zachowania właściwości za pomocą słowa kluczowego zastąpienia . Aby uzyskać więcej informacji na temat tych opcji, zobacz Dziedziczenie.

Właściwość przesłaniająca właściwość wirtualną może być również zapieczętowana, określając, że dla klas pochodnych nie jest już wirtualna. Na koniec właściwość można zadeklarować jako abstrakcyjną. Właściwości abstrakcyjne nie definiują implementacji w klasie, a klasy pochodne muszą napisać własną implementację. Aby uzyskać więcej informacji na temat tych opcji, zobacz Klasy abstrakcyjne i zapieczętowane oraz składowe klasy.

Uwaga

Jest to błąd podczas używania modyfikatora wirtualnego, abstrakcyjnego lub zastępowania metody dostępu do właściwości statycznej.

Przykłady

W tym przykładzie przedstawiono właściwości wystąpienia, statyczne i tylko do odczytu. Akceptuje nazwę pracownika z klawiatury, zwiększa NumberOfEmployees się o 1 i wyświetla nazwę i numer pracownika.

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:
}

Przykład ukrytej właściwości

W tym przykładzie pokazano, jak uzyskać dostęp do właściwości w klasie bazowej ukrytej przez inną właściwość o tej samej nazwie w klasie pochodnej:

public class Employee
{
    private string _name;
    public string Name
    {
        get => _name;
        set => _name = value;
    }
}

public class Manager : Employee
{
    private string _name;

    // Notice the use of the new modifier:
    public new string Name
    {
        get => _name;
        set => _name = value + ", Manager";
    }
}

class TestHiding
{
    public static void Test()
    {
        Manager m1 = new Manager();

        // Derived class property.
        m1.Name = "John";

        // Base class property.
        ((Employee)m1).Name = "Mary";

        System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
        System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
    }
}
/* Output:
    Name in the derived class is: John, Manager
    Name in the base class is: Mary
*/

Poniżej przedstawiono ważne kwestie w poprzednim przykładzie:

  • Właściwość Name w klasie pochodnej ukrywa właściwość Name w klasie bazowej. W takim przypadku new modyfikator jest używany w deklaracji właściwości w klasie pochodnej:
    public new string Name
    
  • (Employee) Rzutowanie służy do uzyskiwania dostępu do ukrytej właściwości w klasie bazowej:
    ((Employee)m1).Name = "Mary";
    

Aby uzyskać więcej informacji na temat ukrywania członków, zobacz nowy modyfikator.

Przykład przesłonięcia właściwości

W tym przykładzie dwie klasy Cube i Square, implementują klasę abstrakcyjną, Shapei przesłaniają jej właściwość abstrakcyjną Area . Zwróć uwagę na użycie modyfikatora przesłonięcia we właściwościach. Program akceptuje stronę jako dane wejściowe i oblicza obszary dla kwadratu i modułu. Akceptuje również obszar jako dane wejściowe i oblicza odpowiednią stronę kwadratu i modułu.

abstract class Shape
{
    public abstract double Area
    {
        get;
        set;
    }
}

class Square : Shape
{
    public double side;

    //constructor
    public Square(double s) => side = s;

    public override double Area
    {
        get => side * side;
        set => side = System.Math.Sqrt(value);
    }
}

class Cube : Shape
{
    public double side;

    //constructor
    public Cube(double s) => side = s;

    public override double Area
    {
        get => 6 * side * side;
        set => side = System.Math.Sqrt(value / 6);
    }
}

class TestShapes
{
    static void Main()
    {
        // Input the side:
        System.Console.Write("Enter the side: ");
        double side = double.Parse(System.Console.ReadLine());

        // Compute the areas:
        Square s = new Square(side);
        Cube c = new Cube(side);

        // Display the results:
        System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
        System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
        System.Console.WriteLine();

        // Input the area:
        System.Console.Write("Enter the area: ");
        double area = double.Parse(System.Console.ReadLine());

        // Compute the sides:
        s.Area = area;
        c.Area = area;

        // Display the results:
        System.Console.WriteLine("Side of the square = {0:F2}", s.side);
        System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
    }
}
/* Example Output:
    Enter the side: 4
    Area of the square = 16.00
    Area of the cube = 96.00

    Enter the area: 24
    Side of the square = 4.90
    Side of the cube = 2.00
*/

Zobacz też