Czy powinieneś robić nieruchomości prywatne?


19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

Widziałem osoby, które tworzą nieruchomości dla każdego członka, prywatne czy nie ... czy to ma jakiś sens? Widziałem, że ma to sens w 1% przypadków, gdy chcesz kontrolować dostęp do członka wewnątrz klasy zawierającej go, ponieważ jeśli nie użyjesz właściwości dla każdego członka, doprowadzi to do niespójności i sprawdzi, czy członek ma dostęp lub nie (ponieważ masz dostęp do obu w ramach klasy).

Odpowiedzi:


17

Krótka odpowiedź: Tak , kiedy jest taka potrzeba. W przeciwnym razie użyj narzędzia do pobierania i ustawiania właściwości Auto-Implemented Propertyprivate string Whatever { get; set;}

  • Jest to bardzo przydatne, gdy korzystasz z podejścia opartego na ścisłej domenie
  • Przydaje się również, gdy podczas ustawiania wartości należy sprawdzić określoną logikę

Oto pełny opis tego, kiedy będziesz używać prywatnych ustawień: użycie właściwości C # .


W razie potrzeby bardzo łatwo jest upublicznić nieruchomość.
Tom Pickles

4
Co to jest podejście oparte na ścisłej domenie ?
Trisped

1
Miałem na myśli prywatnego setera we własności klasy. Powinno to zapobiegać bezpośredniemu dostępowi do ustawionej wartości z obiektu i ułatwić pewne sprawdzenie logiki przed ustawieniem wartości.
Yusubov,

posiadanie zmiennych prywatnych z publicznym ustawiaczem / pobierającymi (nawet jeśli są po prostu naiwne) ma również tę zaletę, że jeśli twój kod zostanie skompilowany w bibliotece, sygnatura pozostanie niezmieniona, jeśli zastąpisz swój naiwny ustawiający / pobierające tymi, które mają side- efekt itp., ale jeśli zamiast tego użyjesz prostej zmiennej publicznej, podpis biblioteki zmieni się, jeśli zmienisz ją na prywatny w / nienaiwny setter / getters. .. co oznaczałoby, że jeśli Twoja biblioteka zmieni się w ten sposób, wówczas konsumenci będą musieli dokonać ponownej kompilacji.
orion elenzil

12

Kilka dobrych odpowiedzi już tutaj, ale myślę, że większość z nich pomija jeden punkt twojego pytania:

Widziałem osoby, które tworzą nieruchomości dla każdego członka, prywatne czy nie ... czy to ma jakiś sens?

Myślę, że rzadko jest to konieczne wcześniej dla każdego członka . Zacząć od

 private string Whatever;

Gdy później dojdziesz do punktu, w którym potrzebujesz enkapsulacji lub warunkowego punktu przerwania dla tego konkretnego elementu członkowskiego, nadal możesz zastąpić go właściwością o tej samej nazwie - w większości przypadków bez zmiany używanego kodu Whatever. Ale uwaga, istnieją subtelne różnice, patrz odpowiedź Briana Rasmussena w tym poście SO (link podany również przez Emmada Kareema, +1).


+1, również masz rację, większość odpowiedzi pominęła oryginalne pytania, jesteśmy typowymi informatykami :)
NoChance

Refaktoryzacja pola po nieruchomości jest koszmarem; wymagając ponownej kompilacji wszystkich klientów. Zmiana właściwości automatycznej na niestandardową logikę to pestka. Jeśli istnieje nawet niewielka szansa, że ​​kod zobaczy refaktor, będzie o wiele mniej pracy, jeśli użyjesz właściwości od samego początku.
Dan

@ Dan: w większości projektów widziałem, że wysiłek jest taki sam, w obu przypadkach musisz dokonać ponownej kompilacji . Niemniej jednak myślę, że masz rację, że używanie właściwości, zwłaszcza właściwości automatycznych, może pomóc w uniknięciu problemów z subtelnymi płytkami z odbiciem lub użyciem zmiennych składowych w parametrach „wyjściowych”.
Doc Brown

@DocBrown W każdym razie zdecydowanie nie jest to stwierdzenie typu catch-all. Jest wiele powodów, aby po prostu to uprościć i użyć pola. Jeśli potrafisz przewidzieć refaktor, nawet subtelne problemy są warte rozważenia.
Dan

7

Jedną z największych zalet tego podejścia jest to, że zyskujesz kontrolę nad zmianami zmiennych .

Rozważ następujące.
Debugujesz duży projekt. Pewna metoda zgłasza wyjątek. Ustawiasz punkt przerwania i ujawniasz, że pewna zmienna składowa ma złą wartość. Powiedz, że twój kod bardzo mocno korzysta z tej zmiennej, a znajdziesz setki sposobów pisania ( scenariusz Spaghetti ). Więc nie możesz dowiedzieć się, które z tych zastosowań przypisano złą wartość.
Jeśli kod jest wielowątkowy, debugowanie może stać się prawdziwym koszmarem.

Za pomocą właściwości można ustawić punkt przerwania w ustawianiu . W połączeniu z warunkiem może zostać przybity w jednym biegu.

VC ++ ma punkty przerwania danych , ale jest dostępny tylko dla kodu niezarządzanego.


1

Jeśli nie korzystasz z właściwości, jedyną inną opcją jest użycie pola do przechowywania danych zmiennych. Właściwości i pola mają znaczące różnice. Większość tych różnic znajduje się w polach vs. właściwości . Proponuję przestudiować różnice, a następnie dokonać wyboru zgodnie z potrzebami aplikacji. Należy jednak pamiętać, że używanie pól zamiast właściwości nie jest powszechną praktyką OO ze względu na ich naturę i wady.


1

Własności prywatne mogą być bardzo przydatne do opisania zachowania rzeczy, które są wewnętrzne dla twojej klasy. To, że są prywatne, nie oznacza, że ​​nie powinieneś korzystać z cukru syntaktycznego, który dają ci właściwości.


Podany przykład jest jednak kiepski. Obiekty pobierające i ustawiające właściwości typu Boilerplate są prawie zawsze złym pomysłem. Jeśli używasz C # 3.0 lub nowszego, właściwości automatyczne są znacznie lepszym pomysłem:

private string Whatever { get; set; };

Jest to krótsze, czystsze i znacznie bardziej czytelne. W rzeczywistości jest to niewiele więcej niż tylko zadeklarowanie zmiennej bazowej.

Co najważniejsze, właściwości automatyczne można w dowolnym momencie przekonwertować na pełne właściwości bez zmiany semantyki reszty programu! Dlatego jeśli chcesz dodać sprawdzanie poprawności lub obsługę błędów, możesz to zrobić z łatwością.


W tej chwili zawsze myślałem, że szkoda, że ​​nie można mieć właściwości tylko do odczytu, aby wymusić jedynie możliwość zapisu do wartości w konstruktorze ( wolę typy niezmienne ), ale zostało to dodane w C # 6.0 jako „Inicjatory automatycznych właściwości”, które umożliwiają:

private string Whatever { get; } = ...;

lub

private string Whatever { get; };

wraz z

    Whatever = ...;

w konstruktorze.


0

Nie jest to absolutnie konieczne w przypadku prywatnych nieruchomości, ale pozwala modyfikować sposób, w jaki właściwość pobiera / ustawia wartość bazową bez zmiany sposobu dostępu.

Na przykład modyfikowanie sposobu ustawiania wartości bazowej:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}

0

Tak

Osobiście używam tego jako mechanizmu buforowania i możemy nazwać to buforowaniem na poziomie właściwości .

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

Teraz w innych miejscach w mojej klasie używam Userswłasności zamiast userspola.

Inną wykonalną sytuacją może być sytuacja, gdy chcesz zaimplementować logikę na polu, ale w sposób scentralizowany w klasie. W ten sposób można zarówno utworzyć GetFoo()metodę, jak i Foowłaściwość dla foopola.

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}

0

Coś jeszcze do rozważenia:

Rzeczywiste właściwości są szczegółami implementacji. Jednym z celów OOP jest próba zminimalizowania ujawnienia szczegółów implementacji tam, gdzie jest to praktyczne.

Powodem tego jest to, że jeśli ukryjesz właściwości i wystawisz je tylko za pomocą metod pobierających i ustawiających, masz znacznie większą kontrolę. Na przykład moduł pobierający może sprawdzić poprawność danych wejściowych i zapobiec ustawieniu właściwości na niepoprawny stan. Jeśli możliwość zmiany wartości ma krytyczne znaczenie dla czasu, wówczas ustawiający może się przed tym zabezpieczyć. Moduł pobierający, z jakiegokolwiek powodu, uzyskanie rzeczywistej wartości może być kosztowne, może wykonać leniwą inicjalizację i / lub buforowanie.

Odsłonięcie obiektów pobierających i ustawiających oraz ukrywanie właściwości oznacza, że ​​czasami nie potrzebujesz żadnej właściwości. Twój program pobierający może obliczyć wartość przy wywołaniu lub przekazać zadanie gdzie indziej lub cokolwiek, co pozwala mu spełnić specyfikację. W twojej klasie ważne są informacje, do których możesz uzyskać dostęp, a nie sposób, w jaki informacje te są reprezentowane wewnętrznie.

Oczywiście, jeśli nie ma żadnych negatywnych konsekwencji dla bezpośredniego dostępu do nieruchomości poza klasą, należy ją po prostu podać do wiadomości publicznej. Jednak z mojego doświadczenia wynika, że ​​takich przypadków jest stosunkowo niewiele i są one bardzo odległe.


0

Wiem, że to stare pytanie i wszystko, co powiedział Yusubov, jest poprawne, ale jeśli chodzi o jego drugi punkt na temat konkretnej logiki w seterach, a ponieważ nie widziałem, aby ktokolwiek specjalnie o tym wspominał, doskonałym przykładem byłby, gdy twoja klasa implementuje INotifyPropertyChangedinterfejs.

Jest to używane w tonach WCF i Silverlight, co pozwala wiedzieć, kiedy konkretna właściwość zmieniła się za pomocą logiki w seterze, jak w poniższej klasie:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
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.