CodeGym /Kursy /C# SELF /Właściwości (Properties) w C#

Właściwości (Properties) w C#

C# SELF
Poziom 17 , Lekcja 1
Dostępny

1. Wprowadzenie

Zacznijmy od problemów, które pojawiają się przy bezpośrednim użyciu pól. Jeśli zadeklarujesz pole jako public, można je zmieniać od razu i bezpośrednio z dowolnego miejsca w programie:

public class Dog
{
    public string Name;
}

Dog dog = new Dog();
dog.Name = ""; // O_o ... to jak "imię psa = pusty string"!

Użytkownik może przypisać polu Name zupełnie absurdalne wartości, nawet takie, które nie mają sensu w twojej domenie: pusty string, za długie imię, albo nawet null. To jakby ktoś składał twoją nową szafę z IKEI, a ty dajesz mu pełny dostęp do fabryki stolarskiej.

Przypominam, że enkapsulacja to wtedy, gdy obiekt sam kontroluje swoje dane. Nie ufamy każdemu przypadkowemu typowi, tylko dajemy specjalne "drzwiczki" — właściwości (Properties), przez które odbywa się dostęp do pola, ale z możliwością sprawdzenia, logowania, modyfikacji albo innych reakcji na zmianę wartości.

2. Definicja i składnia

Właściwość to specjalny członek klasy, który wygląda prawie jak pole, ale pod spodem to para specjalnych metod: getter (pobierz wartość) i setter (ustaw wartość). Dzięki właściwości możemy:

  • Pozwolić (albo zabronić) odczyt/zapis danych;
  • Dodać walidację lub logikę przy dostępie do danych;
  • Ukryć wewnętrzne pole, a nawet trzymać wartość gdzieś indziej.

Właściwość deklaruje się bardzo podobnie do pola, tylko z klamrami i słowami kluczowymi get i set w środku.


[modyfikator_dostępu] typ NazwaWłaściwości
{
    get { ... }
    set { ... }
}
Szablon deklaracji właściwości

Oto przykład dla naszej klasy Dog:

public class Dog
{
    private string _name; // pole wewnętrzne (private!)

    public string Name
    {
        get { return _name; }  // "getter": pobierz imię
        set { _name = value; } // "setter": ustaw imię
    }
}

Uwaga: podkreślenie zwykle używa się dla prywatnych pól (_name). To standard stylu C#.

3. Jak działa właściwość

Właściwość to taki „strażnik”, który stoi między wewnętrznymi danymi obiektu a światem zewnętrznym. Przykład:

Dog dog = new Dog();
dog.Name = "Szarik";
Console.WriteLine(dog.Name);

Wyjaśnienie:

  • Kiedy program dochodzi do linii dog.Name = "Szarik"; — wtedy wywołuje się metoda set właściwości i możesz tam dodać dowolną potrzebną walidację (np. sprawdzić, czy imię nie jest puste).
  • W momencie Console.WriteLine(dog.Name); wywołuje się metoda get, która po prostu zwraca aktualną wartość albo, jeśli trzeba, coś dynamicznie wyliczonego.

Wygląda, jakby to było zwykłe pole, ale tak naprawdę wszystko jest pod kontrolą!

4. Czemu właściwości to "Best Practice"

W większości przypadków nie dajemy bezpośredniego dostępu do wewnętrznych pól obiektu. Nawet jeśli teraz wydaje się, że walidacja nie jest potrzebna, nawyk opakowywania danych we właściwości mega się przydaje, gdy zasady gry się zmienią.

Przykład "walidacji" przy przypisaniu:


public class Dog
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentException("Imię psa nie może być puste!");
            }
            _name = value;
        }
    }
}

Teraz coś takiego:

dog.Name = ""; // Rzuci wyjątek!

… chroni nasz obiekt przed absurdalnymi wartościami.

5. Właściwości: tylko do odczytu, tylko do zapisu i zwykłe

Czasem trzeba pozwolić tylko na podgląd wartości (np. pies ma tylko rok urodzenia, nie można go zmienić), albo odwrotnie — tylko ustawianie (rzadko, ale może chcesz zrobić coś tajemniczego).

  • Tylko do odczytu: piszemy tylko get, usuwamy set.
  • Tylko do zapisu: piszemy tylko set, usuwamy get.

Przykłady:

public class Dog
{
    private int _birthYear = 2018;

    // Tylko do odczytu
    public int BirthYear
    {
        get { return _birthYear; }
    }

    // Tylko do zapisu (bardzo rzadko spotykane)
    public string Secret
    {
        set { /* robimy coś z value */ }
    }
}

6. Właściwości vs pola

Pole Właściwość
Składnia
public string Name;
public string Name { get; set; }
Dostęp Bezpośredni Przez get/set
Walidacja Brak Można dodać w set/get
Rozszerzalność Brak Można modyfikować w każdej chwili
Integracja z IDE Widoczne jako pola Widoczne jako właściwości (ważne dla frameworków)

Ilustracja: Jak działa właściwość

sequenceDiagram
participant User as Użytkownik obiektu
participant Dog as Obiekt Dog
participant Field as Prywatne pole _name

User->>Dog: dog.Name = "Ryżyk"
Dog->>Dog: set Name("Ryżyk")
Dog->>Field: _name = "Ryżyk"

User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: czyta _name
Dog->>User: zwraca "Ryżyk"

7. Typowe błędy i niuanse

Zobaczmy, gdzie mogą czaić się pułapki.

Częsty błąd — pomylić pola i właściwości, przypadkowo wystawić prywatne dane na zewnątrz:

public string name; // To pole! Wszędzie widoczne, niebezpieczne!

Lepiej tak:

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

Statyczne właściwości istnieją, żeby trzymać wartości wspólne dla wszystkich obiektów. Ale używaj ich ostrożnie.

Ciekawostka: jeśli właściwość ma tylko get i żadnego konstruktora, to nie da się jej w ogóle zmienić — to się nazywa immutable property i jest mocno używane w nowoczesnych podejściach do projektowania (wrócimy do tego szerzej w lekcjach o record i niezmienności danych).

Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION