Udostępnij za pośrednictwem


Właściwości (Przewodnik programowania w języku C#)

Właściwość to element członkowski, który zapewnia elastyczny mechanizm odczytu, zapisu lub obliczania wartości pola danych. Właściwości są wyświetlane jako elementy członkowskie danych publicznych, ale są implementowane jako specjalne metody nazywane metodami dostępu. Ta funkcja umożliwia obiektom wywołującym łatwe uzyskiwanie dostępu do danych i nadal pomaga promować bezpieczeństwo i elastyczność danych. Składnia dla właściwości jest naturalnym rozszerzeniem pól. Pole definiuje lokalizację magazynu:

public class Person
{
    public string? FirstName;

    // Omitted for brevity.
}

Automatycznie zaimplementowane właściwości

Definicja właściwości zawiera deklaracje metody get i set , która pobiera i przypisuje wartość tej właściwości:

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

    // Omitted for brevity.
}

W poprzednim przykładzie przedstawiono automatycznie zaimplementowaną właściwość. Kompilator generuje ukryte pole zapasowe dla właściwości . Kompilator implementuje również treść metod dostępu get i set. Wszystkie atrybuty są stosowane do automatycznie zaimplementowanej właściwości. Atrybut można zastosować do pola kopii zapasowej wygenerowanego przez kompilator, określając field: tag atrybutu.

Właściwość można zainicjować na wartość inną niż domyślna, ustawiając wartość po zamykającym nawiasie klamrowym dla właściwości. Możesz preferować początkową wartość FirstName właściwości jako pusty ciąg, a nie null. Należy określić to, jak pokazano w poniższym kodzie:

public class Person
{
    public string FirstName { get; set; } = string.Empty;

    // Omitted for brevity.
}

** Właściwości oparte na polach

W języku C# 13 można dodać walidację lub inną logikę w akcesorze dla właściwości, używając funkcjonalności podglądu słowa kluczowego field. field Słowo kluczowe uzyskuje dostęp do syntetyzowanego pola pomocniczego kompilatora dla właściwości. Umożliwia pisanie metody dostępu do właściwości bez jawnego deklarowania oddzielnego pola zapasowego.

public class Person
{
    public string? FirstName 
    { 
        get;
        set => field = value.Trim(); 
    }

    // Omitted for brevity.
}

Ważne

Słowo kluczowe field jest funkcją zapoznawczą w języku C# 13. Musisz korzystać z platformy .NET 9 i ustawić element <LangVersion> na preview w pliku projektu, aby użyć kontekstowego słowa kluczowego field.

Należy zachować ostrożność przy użyciu funkcji słowa kluczowego field w klasie, która ma pole o nazwie field. Nowe field słowo kluczowe zaciemnia pole o nazwie field w kontekście akcesora właściwości. Możesz zmienić nazwę zmiennej field lub użyć tokenu @ , aby odwołać się do identyfikatora field jako @field. Aby dowiedzieć się więcej, przeczytaj specyfikację słowa kluczowego field.

Wymagane właściwości

W poprzednim przykładzie osoba wywołująca może utworzyć Person przy użyciu konstruktora domyślnego, bez ustawiania właściwości FirstName. Właściwość zmieniła typ na nullable ciąg znaków. Począwszy od języka C# 11, można wymagać od wywołujących ustawienia właściwości:

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName) => FirstName = firstName;

    public required string FirstName { get; init; }

    // Omitted for brevity.
}

Powyższy kod wprowadza dwie zmiany w Person klasie. FirstName Najpierw deklaracja właściwości zawiera required modyfikator. Oznacza to, że każdy kod tworzący nową Person właściwość musi ustawić tę właściwość przy użyciu inicjatora obiektów. Po drugie, konstruktor, który przyjmuje firstName parametr, ma System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute atrybut . Ten atrybut informuje kompilator, że ten konstruktor ustawia wszystkierequired elementy. Wywołujący, którzy używają tego konstruktora, nie muszą ustawiać właściwości required za pomocą inicjatora obiektu.

Ważne

Nie należy mylić required z nienulowalnym. Prawidłowe jest ustawienie required właściwości na null lub default. Jeśli typ jest nienullowalny, taki jak string w tych przykładach, kompilator wyświetla ostrzeżenie.

var aPerson = new Person("John");
aPerson = new Person { FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();

Definicje treści wyrażeń

Metody dostępu do właściwości często składają się z instrukcji jednowierszowych. Metody dostępu przypisują lub zwracają wynik wyrażenia. Te właściwości można zaimplementować jako elementy oparte na wyrażeniach. Definicje treści wyrażeń składają się z tokenu => , po którym następuje wyrażenie, które ma zostać przypisane do właściwości lub pobrane z tej właściwości.

Właściwości tylko do odczytu mogą implementować akcesor get jako członek o treści wyrażeniowej. Poniższy przykład implementuje właściwość Name tylko do odczytu jako członek wyrażeniowy.

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public required string FirstName { get; init; }
    public required string LastName { get; init; }

    public string Name => $"{FirstName} {LastName}";

    // Omitted for brevity.
}

Właściwość Name jest właściwością obliczoną. Nie ma pola tworzenia kopii zapasowej dla elementu Name. Właściwość oblicza to za każdym razem.

Kontrola dostępu

W poprzednich przykładach pokazano właściwości odczytu/zapisu. Można również utworzyć właściwości tylko do odczytu lub przydzielić różne poziomy dostępu dla akcesorów set i get. Załóżmy, że klasa Person powinna umożliwiać zmianę wartości właściwości FirstName tylko za pomocą innych metod wewnątrz klasy. Można nadać dostęp akcesorowi private zamiast internal lub public.

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 Person klasie.

Możesz dodać dowolny restrykcyjny modyfikator dostępu do akcesorów set lub get. Modyfikator dostępu dla pojedynczego akcesora musi być bardziej restrykcyjny niż dostępność właściwości. Powyższy kod jest legalny, ponieważ właściwość FirstName to public, ale akcesor ustawiający to private. Nie można zadeklarować private właściwości z akcesorem public. Deklaracje właściwości można również zadeklarować protected, internal, protected internal, lub nawet private.

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

  • Akcesorium set może mieć init jako modyfikator dostępu. Ten set akcesor może być wywoływany tylko z poziomu inicjalizatora obiektu lub konstruktorów typu. Jest bardziej restrykcyjny niż private na akcesorium set .
  • Właściwość zaimplementowana automatycznie może zadeklarować akcesor get bez akcesora set. W takim przypadku kompilator umożliwia tylko z konstruktorów typu wywoływanie akcesora set. Jest bardziej restrykcyjny niż akcesor init w akcesorze set.

Zmodyfikuj klasę Person w następujący sposób:

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

    public string FirstName { get; }

    // Omitted for brevity.
}

Powyższy przykład wymaga, aby wywołujący używali konstruktora zawierającego FirstName parametr . 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 zmienić metodę dostępu set na metodę dostępu init, jak to 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 razem z modyfikatorem required, aby wymusić właściwą inicjalizację.

Właściwości z polami zapasowymi

Można mieszać koncepcję obliczonej właściwości z polem prywatnym i utworzyć właściwość z buforowaną oceną. Na przykład zaktualizuj FullName właściwość, aby formatowanie ciągu odbywało się przy pierwszym dostępie:

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public required string FirstName { get; init; }
    public required string LastName { get; init; }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

Ta implementacja działa, ponieważ właściwości FirstName i LastName są tylko do odczytu. Użytkownicy mogą zmienić swoje imię i nazwisko. Zaktualizowanie właściwości FirstName i LastName w celu zezwolenia na akcesory set wymaga unieważnienia każdej buforowanej wartości dla fullName. Zmodyfikujesz metody dostępu właściwości FirstName i LastName, aby pole fullName zostało ponownie obliczone:

public class Person
{
    private string? _firstName;
    public string? FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            _fullName = null;
        }
    }

    private string? _lastName;
    public string? LastName
    {
        get => _lastName;
        set
        {
            _lastName = value;
            _fullName = null;
        }
    }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

Ostateczna wersja ocenia właściwość FullName tylko wtedy, gdy jest to potrzebne. Poprzednio obliczona wersja jest używana, jeśli jest prawidłowa. W przeciwnym razie obliczenie aktualizuje buforowane wartości. Deweloperzy korzystający z tej klasy nie muszą znać szczegółów implementacji. Żadne z tych wewnętrznych zmian nie ma wpływu na użycie obiektu Person.

Począwszy od języka C# 13, można tworzyć partial właściwości w partial klasach. Deklaracja implementowania właściwości partial nie może być automatycznie zaimplementowaną właściwością. Właściwość zaimplementowana automatycznie używa tej samej składni co deklaracja częściowej właściwości.

Właściwości

Właściwości są formą pól inteligentnych w klasie lub obiekcie. Z zewnątrz obiektu są one wyświetlane jak pola w obiekcie. Można jednak zaimplementować właściwości przy użyciu pełnej palety funkcji języka C#. Możesz zapewnić walidację, różne ułatwienia dostępu, leniwą ocenę lub wszelkie wymagania wymagane przez scenariusze.

  • Proste właściwości, które nie wymagają niestandardowego kodu dostępu, można zaimplementować jako definicje treści wyrażenia lub automatycznie zaimplementowane właściwości.
  • Właściwości umożliwiają klasie uwidacznianie publicznego sposobu uzyskiwania i ustawiania wartości podczas ukrywania implementacji lub kodu weryfikacyjnego.
  • Metoda get jest używana do zwracania wartości właściwości, a metoda set jest używana do przypisywania nowej wartości. Akcesor właściwości init służy do przypisywania nowej wartości tylko podczas budowy obiektu. Te akcesory mogą mieć różne poziomy dostępu. Aby uzyskać więcej informacji, zobacz Ograniczanie dostępności akcesorów.
  • Słowo kluczowe value służy do definiowania wartości przypisywanej przez set lub init akcesor.
  • Właściwości mogą być do odczytu i zapisu (mają zarówno get metodę dostępu, jak i set metodę dostępu), tylko do odczytu (mają get metodę dostępu, ale bez set metody dostępu) lub tylko do zapisu (mają set metodę dostępu, ale bez get metody dostępu). Właściwości tylko do zapisu są rzadkie.

Specyfikacja języka C#

Aby uzyskać więcej informacji, zobacz Właściwości w specyfikacji języka C#. Specyfikacja języka jest ostatecznym źródłem informacji o składni i użyciu języka C#.

Zobacz też