zrozumienie seterów prywatnych


94

Nie rozumiem potrzeby posiadania prywatnych ustawiaczy, które zaczęły się od C # 2.

Posiadanie metody ustawiającej to dla mnie pozwolenie użytkownikowi na ustawienie pewnych zmiennych w tej klasie. W ten sposób nie będziemy ujawniać zmiennych bezpośrednio użytkownikom. Zamiast tego pozwoliliśmy im to zrobić za pomocą tej publicznej metody ustawiającej.

Dla mnie jest to „hermetyzacja”. Istnieje kilka argumentów, które twierdzą, że prywatne setery pozwolą ci zastosować hermetyzację.

Czy nie używam hermetyzacji przy użyciu publicznych metod ustawiających? Dlaczego potrzebujemy prywatnych seterów?

Jaka jest różnica między niezmienną klasą a klasą z ustawieniami prywatnymi?


1
Bardzo mi się podobały setery prywatne - pomogło mi zmienić czynniki na brzydkie klasy. Robią też niemożliwym do zadeklarować i ustanowił zakaz stałej zmiennej instancji naraz tak: private File settingsFile = null;a potem w jednym z konstruktorów: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Czasami płakałem po takiej refaktoryzacji kodu :). To, że możesz ustawić element członkowski przed konstruktorem, nie oznacza, że ​​powinieneś, ponieważ w przypadku wielu konstruktorów trudno jest postępować zgodnie z logiką. Prywatne metody ustawiające wymuszają ustawienie wartości wewnątrz konstruktora lub później.
Hamish Grubijan

Odpowiedzi:


270

Logicznie.

Obecność setera prywatnego wynika z faktu, że możesz użyć właściwości automatycznej:

public int MyProperty { get; set; }

Co byś zrobił, gdybyś chciał, aby był tylko do odczytu?

public int MyProperty { get; }

O kurcze!! Nie mogę uzyskać do niego dostępu z mojej własnej klasy; Powinienem stworzyć to jak normalną właściwość:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Hmm ... ale straciłem funkcję „Auto Property” ...

public int MyProperty { get; private set; }

AHHH .. to jest lepsze !!


1
Dzięki. To znowu ma sens
Dene

4
@ktutnik Dziękuję za ułożenie tego w sposób, w jaki to zrobiłeś. Dla mnie też ma to teraz sens!
Vivek M. Chawla

4
Świetnie ilustrowana odpowiedź.
imnk

3
Oh crap!! I can't access it from my own classPocząwszy od C # 6,0 jest to prawdziwe tylko poza fazą inicjalizacji. Zobacz moją odpowiedź stackoverflow.com/a/34223746/198797
tsemer

1
Dodanie do odpowiedzi # tsemer, z c # 6, {get; }NIE jest równoważne z { get; private set; }. Na pierwszy sposób property.GetSetMethod(true)wraca, nulla drugi true. To mnie zaskoczyło.
emragins

38

Metoda ustawiająca prywatna jest przydatna, jeśli masz właściwość tylko do odczytu i nie chcesz jawnie deklarować zmiennej bazowej.

Więc:

public int MyProperty
{
    get; private set;
}

jest taki sam jak:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Dla niezarejestrowanych auto wdrożone właściwości daje spójny sposób ustawienie właściwości od wewnątrz klasy tak, że jeśli potrzebujesz walidacji itd. Masz tylko to jedno miejsce.

Aby odpowiedzieć na twoje ostatnie pytanie, MSDN ma do powiedzenia na temat prywatnych ustawiaczy:

Jednak w przypadku małych klas lub struktur, które po prostu hermetyzują zestaw wartości (danych) i mają niewiele zachowań lub nie mają ich wcale, zaleca się, aby obiekty były niezmienne przez zadeklarowanie metody dostępu set jako prywatnego.

Ze strony MSDN we właściwościach zaimplementowanych automatycznie


1
Przepraszam, że nadal nie widzę wartości dodanej z powodu posiadania prywatnych seterów. Jeśli nie chcemy narażać setera, mamy tylko getter. Jeśli chcemy dodać walidator, możemy mieć publiczny ustawiacz i dodać do niego walidację. Dlaczego potrzebujemy ustawiacza, który nie jest dostępny? Sposób, w jaki to rozumiem, przypomina „Weź ten samochód, ale nie możesz nim jeździć”, dlaczego chcesz mi dać ten samochód, skoro i tak nie mam zamiaru nim jeździć
Dene

@Dene - Z pewnością możesz to zrobić, ponieważ nie jest to złe. Posiadanie właściwości zaimplementowanych automatycznie nie jest obowiązkowe.
ChrisF

Wiem, że nie ma nic złego w tym, jak to wyraziłem. Chodzi mi tylko o to, żeby docenić ulepszenia dokonane przez C # 2. Wydaje się, że wokół tego jest dużo szumu, ale po prostu nie czuję tego ani nie widzę wartości.
Dene

@Dene, przegapiłem cały szum wokół tego. Ale kiedy w końcu zobaczyłem, że jest to możliwe, byłem szczęśliwy, ponieważ wiedziałem, jak wyczyścić niektóre długie klasy z ery .Net 1.1. Chociaż prawdopodobnie możesz użyć wartości właściwości, która nie została jeszcze ustawiona, jest to mniej naturalne niż użycie wartości zmiennej składowej instancji, która została ustawiona na null.
Hamish Grubijan

Upraszcza kod. Podobnie jak właściwości samochodowe. Wiele kolejnych aktualizacji C # dotyczy uczynienia kodu bardziej zwięzłym, a tym samym czytelnym. Jeśli chcesz, nadal możesz robić rzeczy w inny sposób - kompatybilność wsteczna również wydaje się być dużym celem. Myślę, że to kwestia gustu.
niico

18

To raczej proste. Ustawienia prywatne umożliwiają tworzenie publicznych lub chronionych właściwości tylko do odczytu.

Otóż ​​to. To jedyny powód.

Tak, możesz utworzyć właściwość tylko do odczytu, określając tylko metodę pobierającą, ale w przypadku właściwości z automatyczną implementacją wymagane jest określenie zarówno metody pobierania, jak i ustawiania, więc jeśli chcesz, aby właściwość zaimplementowana automatycznie była tylko do odczytu, musisz użyć setery prywatne. Nie ma innego sposobu, aby to zrobić.

Prawdą jest, że metody ustawiające prywatne nie zostały stworzone specjalnie dla automatycznie implementowanych właściwości tylko do odczytu, ale ich użycie jest nieco bardziej ezoteryczne z innych powodów, głównie koncentrując się na właściwościach tylko do odczytu oraz zastosowaniu odbicia i serializacji.


2
Dzięki „jeśli chcesz, aby właściwość zaimplementowana automatycznie była tylko do odczytu, musisz użyć ustawień prywatnych”. To ma dla mnie sens
Dene

18

Wraz z wprowadzeniem języka C # 6,0 i składni inicjatorów automatycznych właściwości prywatne metody ustawiające nie są już potrzebne dla właściwości, które są ustawiane tylko podczas inicjalizacji, w tekście lub w konstruktorze.

Teraz kompilują się te nowe składnie:

Inline zainicjowana właściwość

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Właściwość zainicjowana konstruktora

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, rzeczywiście nie. OP też o to nie pyta. Po prostu nie rozumie potrzeby ich posiadania. To odpowiada: „nie musisz ich już mieć w tym scenariuszu”.
tsemer

6

Nie rozumiem potrzeby posiadania prywatnych ustawiaczy, które zaczęły się od C # 2.

Na przykład klasa faktury umożliwia użytkownikowi dodawanie lub usuwanie towarów z właściwości Pozycje, ale nie pozwala użytkownikowi na zmianę odniesienia Pozycje (tzn. Użytkownik nie może przypisać właściwości Pozycje do innej instancji obiektu listy pozycji).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1 za podkreślenie, że właściwościami można manipulować przez publiczny getter, nawet jeśli mają one ustawiania prywatnego.
user1725145

4

Powiedzmy na przykład, że nie przechowujesz rzeczywistej zmiennej we właściwości ani nie używasz wartości do obliczenia czegoś.

W takim przypadku możesz utworzyć metodę, aby wykonać obliczenia

private void Calculate(int value)
{
 //...
}

Lub możesz to zrobić za pomocą

public int MyProperty {get; private set;}

W takich przypadkach zalecałbym użycie późniejszego, ponieważ właściwości refaktoryzują każdy nienaruszony element członkowski.

Poza tym, jeśli nawet powiesz, że mapujesz właściwość za pomocą zmiennej. W takim przypadku w swoim kodzie chcesz napisać w ten sposób:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Powyższy kod wygląda okropnie, ponieważ programista musi zawsze ostrożnie używać MyProperty dla Get i myprop dla Set.

Aby zachować spójność, możesz użyć setera prywatnego, który sprawia, że ​​właściwość jest tylko do odczytu na zewnątrz, podczas gdy możesz użyć jej ustawiacza wewnątrz swojego kodu.


3

Myślę, że wokół tego tańczyło kilka osób, ale dla mnie wartość prywatnych ustawiaczy polega na tym, że można ująć zachowanie właściwości, nawet w ramach klasy. Jak zauważył abhishek, jeśli chcesz uruchomić zdarzenie zmiany właściwości za każdym razem, gdy właściwość się zmienia, ale nie chcesz, aby właściwość była publicznie odczytywana / zapisywana, musisz albo użyć ustawienia prywatnego, albo podnieść zdarzenie w dowolnym miejscu, w którym zmodyfikujesz pole zapasowe. Ta ostatnia jest podatna na błędy, ponieważ możesz zapomnieć. W związku z tym, jeśli aktualizacja wartości właściwości powoduje wykonanie pewnych obliczeń lub modyfikację innego pola, lub leniwą inicjalizację czegoś, wtedy będziesz również chciał zawrzeć to w ustawieniu prywatnym, zamiast pamiętać, aby robić to wszędzie, gdzie robisz wykorzystanie pola podkładowego.


2

Hermetyzacja oznacza, że ​​stan obiektu zachodzi tylko przez zdefiniowany interfejs, dzięki czemu klasa może upewnić się, że ten stan jest zawsze prawidłowy i zgodny z celem klasy.

Dlatego w niektórych przypadkach doskonale wpisuje się zasada hermetyzacji, aby po prostu ujawnić pole publicznie - wszystkie możliwe wartości pola są poprawne z wszystkimi innymi możliwymi wartościami wszystkich innych pól, a zatem programista może aktywnie zdecydować o zezwoleniu na pole do swobodnej manipulacji przez zewnętrzny kod.

Te przypadki są jednak w większości ograniczone do klas, które są przeważnie „zwykłymi starymi danymi”. Nie są też zbyt interesujący pod tym względem, więc dość o nich.

W innych przypadkach, w innych językach, można by mieć metodę pobierającą i ustawiającą, coś w rodzaju int getId()uzyskiwania wartości i void setId(int val)aktualizowania jej.

Właściwości pozwalają nam używać tej samej składni do czytania i pisania metodami, jakich używalibyśmy do odczytu i zapisu pola. To dobry cukier syntaktyczny, chociaż nie jest niezbędny.

(Właściwie ze względu na sposób, w jaki działa odbicie i przypadki takie jak DataBinder.Evalprzydatne może być posiadanie właściwości nawet wtedy, gdy pole działałoby dobrze, ale to inna sprawa).

Dopóki nie zostaną wprowadzone metody ustawiające prywatne (właściwie to, co zmieniło się w C # 2, to składnia ustawiania prywatnego i publicznego lub chronionego metody pobierającej w tym samym bloku), moglibyśmy mieć metodę prywatną do wykonywania pracy ustawiającej prywatnej, więc setery prywatne nie są naprawdę potrzebne. Są jednak przydatne, więc chociaż są tylko cukrem syntaktycznym, są całkiem przydatne.

Hermetyzacja nie jest kwestią tego, czy twoje setery (lub gettery) są publiczne, prywatne, chronione czy wewnętrzne, ale kwestią tego, czy są odpowiednie . Zacznij od ustawienia domyślnego, że każde pole jest prywatne (i jeśli o to chodzi readonly), a następnie w razie potrzeby dodaj elementy członkowskie (właściwości lub metody), które zmieniają te pola, i upewnij się, że obiekt pozostaje ważny, gdy się zmieniają . Zapewnia to zachowanie niezmiennika klasy , co oznacza, że ​​reguły opisujące prawidłowy zestaw stanów, w których może się znajdować, nigdy nie są łamane (konstruktorzy również pomagają, upewniając się, że zaczyna się w takim prawidłowym stanie).

Jeśli chodzi o ostatnie pytanie, być niezmienne oznacza, że klasa nie ma społeczeństwa, zabezpieczonej lub ustawiaczy wewnętrznych i bez publiczności, zabezpieczonej lub metody wewnętrznych, które zmieniają żadnych pól. Istnieją stopnie tego, w C # są możliwe trzy stopnie:

  1. Wszystkie pola instancji klasy są takie readonly, dlatego nawet kod prywatny nie może ich zmienić. Gwarantuje się, że będzie niezmienny (nic, co spróbuje go zmienić, nie zostanie skompilowane) i prawdopodobnie można na jego podstawie dokonać optymalizacji.

  2. Klasa jest niezmienna z zewnątrz, ponieważ żaden publiczny element członkowski niczego nie zmienia, ale nie ma gwarancji, że użycie readonlynie zostanie zmienione od wewnątrz.

  3. Klasa jest niezmienna, jak widać z zewnątrz, chociaż pewien stan jest zmianą jako szczegół implementacji. Np. Pole mogłoby zostać zapamiętane, a zatem, podczas gdy z zewnątrz próba uzyskania go po prostu pobiera tę samą wartość, pierwsza taka próba faktycznie ją oblicza, a następnie przechowuje do odzyskania przy kolejnych próbach.


1

Potrzebujesz prywatnego ustawiacza, jeśli chcesz obsługiwać następujący scenariusz (nie tylko z tego powodu, ale to powinno wskazywać jeden dobry powód): Masz właściwość, która jest tylko do odczytu w Twojej klasie, tj. Tylko sama klasa może się zmieniać go, ale może go zmienić po utworzeniu instancji. W przypadku powiązań należy następnie uruchomić zdarzenie PropertyChanged, najlepiej jest to zrobić w (prywatnym) programie ustawiającym właściwości. Właściwie możesz po prostu odpalić zdarzenie PropertyChanged z innego miejsca w klasie, ale używanie do tego prywatnego setera to „dobre obywatelstwo”, ponieważ nie rozprowadzasz wyzwalaczy zmiany właściwości w całej klasie, ale trzymasz je w nieruchomość, do której należy.


1

Tak, używasz hermetyzacji przy użyciu właściwości, ale hermetyzacja ma więcej niuansów niż tylko przejęcie kontroli nad sposobem odczytywania i zapisywania właściwości. Odmawianie właściwości do ustawienia spoza klasy może być przydatne zarówno ze względu na niezawodność, jak i wydajność.

Niezmienna klasa to klasa, która nie zmienia się po utworzeniu, więc do ochrony właściwości są potrzebne prywatne metody ustawiające (lub w ogóle nie ustawiające).

Prywatne metody ustawiające były częściej używane ze skrótem właściwości, który został wprowadzony w C # 3. W C # 2 setter był często po prostu pomijany, a prywatne dane były dostępne bezpośrednio po ustawieniu.

Ta nieruchomość:

public int Size { get; private set; }

jest taki sam jak:

private int _size;
public int Size {
  get { return _size; }
  private set { _size = value; }
}

z wyjątkiem tego, że nazwa zmiennej bazowej jest wewnętrznie tworzona przez kompilator, więc nie można uzyskać do niej bezpośredniego dostępu.

W przypadku właściwości skróconej do utworzenia właściwości tylko do odczytu potrzebny jest ustawiacz prywatny, ponieważ nie można uzyskać bezpośredniego dostępu do zmiennej bazowej.


Jeśli dobrze pamiętam, nie można mieć różnych modyfikatorów dostępu w C # 2.0 geti setwcześniejszych. Myślę też, że mieszasz wersje 2.0 i 3.0, ponieważ automatycznie zaimplementowanym skrótem, o którym mówisz, było 3.0.
Anthony Pegram

1

Nie rozumiem potrzeby posiadania prywatnych ustawiaczy, które zaczęły się od C # 2.

Przykład użycia:

Mam wystąpienie obiektu aplikacji, 'UserInfo'który zawiera właściwość SessionTokenIDV1, której nie chcę ujawniać konsumentom mojej klasy.

Potrzebuję również możliwości ustawienia tej wartości z mojej klasy.

Moim rozwiązaniem było hermetyzowanie właściwości, jak pokazano, i ustawienie setera jako prywatnego, aby móc ustawić wartość tokenu sesji bez zezwalania na utworzenie instancji kodu, aby go również ustawić (lub nawet zobaczyć w moim przypadku)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Edycja: Naprawiono kod Tag Edycja: Przykład zawiera błędy, które zostały poprawione


-1

Kredyty dla https://www.dotnetperls.com/property .

prywatne metody ustawiające są takie same jak pola tylko do odczytu. Można je ustawić tylko w konstruktorze. Jeśli spróbujesz ustawić z zewnątrz, otrzymasz błąd czasu kompilacji.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Zobacz poniższy zrzut ekranu, kiedy próbowałem ustawić go spoza klasy.

wprowadź opis obrazu tutaj

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.