Zmienna prywatna a własność?

41

Podczas ustawiania wartości zmiennej w klasie przez większość czasu mamy dwie opcje:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Czy istnieje konwencja, która określa, w jaki sposób powinniśmy przypisywać wartości do zmiennych wewnątrz naszych klas? Na przykład, jeśli mam metodę w tej samej klasie, powinienem ją przypisać za pomocą właściwości lub zmiennej prywatnej. Widziałem to w obie strony, więc zastanawiałem się, czy to jest wybór, czy wydajność jest czynnikiem (prawdopodobnie niewielkim).

Edward
źródło

Odpowiedzi:

23

Zrobiłbym krok dalej i przedstawiłbym 3 przypadki. Mimo że są różne dla każdego, to są reguły, których używam przez większość czasu podczas programowania w C #.

W przypadku 2 i 3 zawsze idź do Accessor Accessor (nie zmienna pola). W przypadku 1 jesteś uratowany nawet przed koniecznością dokonania tego wyboru.

1.) Nieruchomość niezmienna (przekazana konstruktorowi lub utworzona w czasie budowy). W tym przypadku używam zmiennej pola z właściwością tylko do odczytu. Wybieram to w stosunku do prywatnego setera, ponieważ prywatny seter nie gwarantuje niezmienności.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) Zmienna POCO. Prosta zmienna, która może uzyskać / ustawić w dowolnym zakresie publicznym / prywatnym. W takim przypadku użyłbym po prostu właściwości automatycznej.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Właściwości wiązania ViewModel. Dla klas, które obsługują INotifyPropertyChanged, myślę, że potrzebujesz prywatnej zmiennej pola bazowego.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}
Sheldon Warkentin
źródło
2
+1 dla przykładu MVVM. Właściwie to właśnie to wywołało pytanie.
Edward
4
+1: Wymieszaj 2/3 z AOP i masz wspaniały sposób na użycie INPC. [Powiadomienia] public int Foo {get; zestaw; }
Steven Evers
1
@ Job Dla każdej klasy uzyskującej dostęp do klasy, prywatny ustawiacz jest wystarczający do niezmienności. Jednak w klasie prywatny seter nie uniemożliwia powtórnego ustawienia wartości po początkowej konstrukcji. Funkcja językowa, taka jak „prywatny zestaw tylko do odczytu”, może koncepcyjnie obejść ten problem, ale nie istnieje.
Sheldon Warkentin
1
Jestem nowym do C #, więc powiedz mi, dlaczego stosowanie public int Foo {get; set;}zamiast public int Foo?
1
Jeśli klasa lub struktura będzie zachowywać się jak POCO lub PODS, jaka jest prawdziwa zaleta zawijania pól we właściwościach? W pełni rozumiem, że zawijanie pól we właściwościach jest przydatne, gdy klasa lub struktura potrzebuje, lub może w przyszłości będzie wymagać utrzymania niezmienników w odniesieniu do jej zawartości (być może poprzez zapewnienie, że inne obiekty zostaną zaktualizowane, aby pasowały do ​​nich), ale jeśli klasa lub struktura określa, że ​​konsumenci mogą zapisywać dowolne wartości w dowolnej kolejności bez ograniczeń i skutków ubocznych, jakie użyteczne zachowania można by dodać do członka-członka?
supercat
18

Ogólnie powiedziałbym, że przypisuj do pola w konstruktorze i używaj tej właściwości wszędzie indziej. W ten sposób, jeśli ktoś doda funkcjonalność do nieruchomości, nigdzie jej nie przegapisz.

Z pewnością nie jest to czynnik wydajności. Optymalizator wstawi dla Ciebie proste polecenie get lub set, a końcowy kod MSIL prawdopodobnie będzie identyczny.

pdr
źródło
Czy jest jakiś konkretny powód użycia pola w konstruktorze? Mniejsza szansa na dziwne skutki uboczne?
Podpisz
4
@Sign: Domyślam się, że jeśli weryfikacja nieruchomości ma miejsce (teraz lub w przyszłości), nie chcesz ryzykować niepowodzenia weryfikacji podczas budowy. Walidacja nie jest logiczna na tym etapie, ponieważ nie można zagwarantować, że obiekt będzie stabilny, dopóki konstruktor się nie zakończy.
Steven Evers
@Sign: Zarówno to, co powiedziałeś, jak i to, co powiedział Snorfus. Lub jeśli chcę rejestrować zmiany we właściwości, prawdopodobnie nie chcę rejestrować ustawienia początkowego. Ale powiedziałem „ogólnie”.
pdr
3
@Sign: problem polega na tym, że: jeśli metoda set właściwości może zostać zastąpiona w podklasie, możesz wywołać efekt uboczny podczas tworzenia obiektu lub niespójnego obiektu (tj. Przesłonięta właściwość jest zaprogramowana tak, aby nie ustawiać żadnej wartości dla to pole). Używanie właściwości w konstruktorach jest bezpieczne tylko wtedy, gdy ustawiona metoda jest prywatna lub klasa jest zapieczętowana.
Diego
4

Zależy.

Najpierw powinieneś preferować właściwości automatyczne, jeśli to możliwe:

public string MyValue {get;set;}

Po drugie, lepszym podejściem byłoby prawdopodobnie użycie właściwości, jeśli masz jakąkolwiek logikę, prawdopodobnie powinieneś sam przez nią przejść, szczególnie jeśli ta logika jest synchronizacją wątków.

Ale powinieneś również wziąć pod uwagę, że może to nieco pogorszyć twoją wydajność, jeśli niepoprawnie synchronizujesz, możesz sam się zakleszczyć, a czasem właściwą ścieżką jest obejście logiki we właściwości.

AK_
źródło
3
Lubię też public string MyValue {get; private set;}.
Job
3

Najprościej byłoby po prostu przypisać ją do samej zmiennej, ponieważ jesteś w metodzie klasy i kontrolujesz zachowanie klasy.

Ale sedno własności polega na tym, że usuwają one zmienną. Podczas gdy taka prosta właściwość, jak w twoim przykładzie, nie ma żadnego zastosowania w stosunku do zwykłej publicznej zmiennej członka, właściwości zwykle robią (lub powinny robić) dodatkowe rzeczy w swoich obiektach pobierających i ustawiających. A jeśli chcesz, aby te rzeczy były wykonywane automatycznie podczas zmiany właściwości w klasie, to oczywiście czystsze jest działanie na właściwości zamiast zmiennej, aby nie musiała zmieniać każdego przypisania zmiennej, gdy zmienia się zachowanie ustawienia właściwości.

Musisz po prostu uzasadnić to koncepcyjnie. Właściwość jest w rzeczywistości uchwytem umożliwiającym dostęp do pewnego wewnętrznego stanu obiektu, który może składać się z więcej niż jednej zmiennej składowej. Musisz więc zadać sobie pytanie, czy chcesz zmienić tylko podstawowy stan wewnętrzny (lub tylko jego część), czy też właściwość abstrakcyjną reprezentującą ten stan w całości, a w większości przypadków jest to ten ostatni, ponieważ zwykle chcesz, aby Twój obiekt zawsze miał spójny stan.

Chris mówi Przywróć Monikę
źródło
2

Jeśli istnieje jakakolwiek szansa, że ​​implementacja get / set właściwości zmieni się czasem później (na przykład chcesz wywołać zdarzenie podczas wywoływania setlub dodasz leniwy mechanizm oceny później do swojej getfunkcji), może to być dobry pomysł że twój kod wewnątrz klasy będzie używał tej właściwości w prawie wszystkich przypadkach, z wyjątkiem - najprawdopodobniej rzadkich - przypadków, w których wyraźnie nie chcesz, aby używane były te zdarzenia lub leniwe mechanizmy oceny.

W każdym razie, cokolwiek zrobisz, istnieje duża szansa, że ​​jeśli później zmienisz implementację właściwości w taki sposób, będziesz musiał spojrzeć na wszystkie miejsca w klasie, które uzyskują dostęp do tych właściwości, aby sprawdzić, czy naprawdę dostęp do właściwości lub należy zastosować zmienną prywatną.

Doktor Brown
źródło
2

Zawsze korzystam z własności publicznej.

Często do logiki dodawana jest pewna logika, która powinna zawsze działać, gdy właściwość jest ustawiona set, i zamiast tego ustawiając pole prywatne, ustawiacz publiczny omija tam dowolną logikę.

Masz komentarz na temat MVVM prowadzący do tego pytania i uważam, że jest to jeszcze ważniejsze podczas pracy z MVVM. Wiele obiektów wywołuje PropertyChangepowiadomienie ustawiające, a inne obiekty mogą zasubskrybować to zdarzenie, aby wykonać akcję po zmianie określonych właściwości. Jeśli ustawisz zmienną prywatną, akcje te nigdy się nie wykonają, chyba że ręcznie podniesiesz PropertyChangedzdarzenie.

Rachel
źródło
+1 Tak w większości przypadków (MVVM) zdarzenie PropertyChanged jest koniecznością. I to może zostać zwolnione tylko wewnątrz nieruchomości. Dobre wytłumaczenie.
Edward
1

Zasadniczo od Ciebie zależy, co powinieneś zrobić z właściwością i jej polem zaplecza podczas pobierania / ustawiania.

Najczęściej, aby zachować spójność w całym kodzie, powinieneś używać publicznych akcesorów wszędzie tam, gdzie są one dostępne i odpowiednie. Pozwala to na refaktoryzację przy minimalnej zmianie kodu; jeśli metoda wykonująca to ustawienie musi zostać usunięta z klasy i umieszczona gdzie indziej, gdzie pole zaplecza nie jest już dostępne (jak klasa podstawowa), kogo to obchodzi? Używasz czegoś, co jest dostępne wszędzie tam, gdzie sama klasa ma wykonać zadanie. Pole zaplecza w większości przypadków stanowi szczegół implementacji; nikt poza twoją klasą nie powinien wiedzieć, że istnieje.

Główną sytuacją, o której mogę pomyśleć, kiedy powinieneś użyć pola zaplecza, a NIE akcesora właściwości, jest to, że akcesor ma dodatkową logikę (sprawdzanie poprawności lub aktualizację innych informacji o stanie w klasie), której nie chcesz uruchamiać. Przykładem jest początkowa populacja obiektu; możesz mieć klasę, która używa dwóch wartości właściwości do obliczenia trzeciej, która jest również przechowywana w polu kopii zapasowej (ze względu na trwałość). Podczas inicjowania nowej kopii tego obiektu podanej w bazie danych, akcesory właściwości, którzy ponownie obliczają trzecią wartość, mogą narzekać, jeśli druga potrzebna wartość nie jest ustawiona. Używając pól bazowych do ustawiania początkowych wartości tych dwóch (lub trzech) właściwości, omija się logikę sprawdzania poprawności / obliczeń, dopóki instancja nie będzie wystarczająco spójna, aby logika działała normalnie.

KeithS
źródło
0

Zawsze używaj tego, który ma sens. Tak, wiem, że to brzmi dość fałszywie do tego stopnia, że ​​nie ma odpowiedzi.

Istotą właściwości jest zapewnienie interfejsu, dzięki któremu można bezpiecznie uzyskać dostęp do modelu danych. W większości sytuacji zawsze chcesz bezpiecznie uzyskać dostęp do modelu danych za pośrednictwem tego interfejsu, na przykład:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Ale w innych sytuacjach możesz po prostu użyć właściwości jako widoku modelu danych:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Jeśli użycie radianów ma sens SomeAngle, to z całą pewnością skorzystaj z niego.

Na koniec wypij swoją własną pomoc kool. Interfejs API dostępny publicznie powinien być wystarczająco odporny na pracę wewnętrzną.

zzzzBov
źródło