Czy powinienem preferować nieruchomości z polami prywatnymi lub bez nich?

16

Baza kodów, w której teraz pracuję, ma konwencję korzystania z prywatnych pól i właściwości publicznych. Na przykład większość klas ma takich członków zdefiniowanych w ten sposób:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Wiem, że można je przepisać bez ich wewnętrznych właściwości prywatnych:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Chciałbym coś w tym zakresie:

  • Czy jest dobry powód, aby preferować starsze, bardziej wyraźne style niż nowsze, bardziej zwięzłe?
  • Czy powinienem pisać nowe klasy w zwięzłym stylu, czy powinienem starać się dopasować starszy kod w celu zachowania spójności? Czy w tym przypadku spójność jest wystarczająca, aby uzasadnić starszy format?
KChaloux
źródło
Zobacz to pytanie i odpowiedź .
Peter K.,
@PeterK. Informacyjny. Nie odpowiada, czy powinienem martwić się utrzymaniem stylu pozostałej części programu, czy też jest to wystarczająco mały szczegół, aby nie mieć znaczenia.
KChaloux,
@KChalhal: Zrozumiano! Dlatego jest to komentarz, a nie odpowiedź. :-)
Peter K.
@PeterK. Fair 'nuff = p
KChaloux
1
innym powodem, aby się nie zmieniać, jest to, że coś jest przekazywane przez WCF - właściwości automatyczne mają problemy z kontraktem (z powodu nieprawidłowych znaków w nazwach pól zaplecza utworzonych przez .NET)
Leon

Odpowiedzi:

16

Istnieje kilka przypadków, w których wymagany jest tak zwany „starszy” styl:

Odp .: Niezmienne typy wykorzystujące niezmienność podaną w języku. readonlyModyfikator C # zawiesza się tę wartość po zakończeniu budowy. Nie ma możliwości naśladowania tego za pomocą automatycznie implementowanych właściwości (jeszcze).

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Twoi pobierający / ustawiający mają jakąkolwiek logikę. Kod WPF i Silverlight (i podobne), które są powiązane z danymi w twoich klasach, będą implementować w następujący INotifyPropertyChangedsposób:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Poza tym powiedziałbym, że użyj nowego stylu. Utrzymuje zwięzłość kodu i oferuje mniejszą powierzchnię do wkradania się błędów. Ponadto, jeśli kiedykolwiek trzeba zmienić, aby uwzględnić logikę, nie będzie niezgodny z podpisem.

Jesse C. Slicer
źródło
2
Wydaje się, że wszyscy są zgodni co do nowego stylu. Dostajesz +1 i odpowiedź za powiedzenie mi o potrzebie pola z readonlymodyfikatorem, którego nie znałem. = D
KChaloux
3
Niektórzy (w tym ja) uważają właściwości automatyczne z prywatnym akcesorium za „wystarczająco dobre” dla niezmiennych właściwości. To prawda, że ​​mogą one nie być niezmienne, ale musisz tylko uważać, aby nie używać setera poza konstruktorem.
svick,
2
innym powodem jest to, że jeśli chcesz przekazać wartość, refktóra nie działa z właściwościami, potrzebujesz pola
stijn
1
Narzędzie takie jak Fody może zostać wdrożone INotifyPropertyChangedbez potrzeby jawnego tworzenia kopii zapasowych pól. github.com/Fody/PropertyChanged
Sebastian Redl
3
Uwaga: C # 6 pozwala na „auto-właściwości tylko getterów”, które zapewniają w jednym wierszu to, co robi mój przykład „A”.
Jesse C. Slicer,
6

Powiedziałbym, że posiadanie wyraźnego pola kopii zapasowej jest po prostu bezużytecznym cruft: sprawia, że ​​twój kod jest dłuższy i trudniejszy do odczytania. Dodaje także potencjał błędu:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Myślę, że taki błąd może się zdarzyć dość łatwo i będzie trudny do wykrycia.

A jeśli chcesz spójności, zrób to w drugą stronę: zmień kod wykorzystujący pola zapasowe na kod korzystający z właściwości automatycznych. Jest to szczególnie łatwe, jeśli używasz narzędzia refaktoryzującego, takiego jak ReSharper (a jeśli nie używasz czegoś takiego, myślę, że powinieneś zacząć).

Oczywiście nadal istnieją przypadki, w których konieczne jest posiadanie pola zaplecza, ale myślę, że nie ma to związku z tym pytaniem.

svick
źródło
Zrobiłem to na kilku starych polach podczas refaktoryzacji kodu ... to jest ból.
KChaloux,
1

Chociaż pytanie jest dość stare, pomyślałem o dodaniu kilku punktów, które przeczytałem z książki, o której warto tu wspomnieć.

  1. Mechanizmy serializacji w czasie wykonywania zachowują nazwę pliku w serializowanym strumieniu. Nazwa pola bazowego dla właściwości Automatycznie zaimplementowanej (AIP) jest określana przez kompilator i może faktycznie zmieniać nazwę tego pola bazowego za każdym razem, gdy ponownie kompilujesz kod, co neguje możliwość deserializacji instancji dowolnego typu, które zawierają AIP Dlatego nie używaj AIP z żadnym typem, który zamierzasz serializować lub deserializować.

  2. Podczas debugowania nie można ustawić punktu przerwania dla metody AIP get lub set, więc nie można łatwo wykryć, kiedy aplikacja pobiera lub ustawia tę właściwość. Możesz ustawić punkty przerwania na ręcznie zaimplementowanych punktach przerwania

Baran
źródło
3
# 1 - Większość serializatorów domyślnie ignoruje prywatne właściwości / pola; Nigdy nie spotkałem wspomnianych problemów. # 2 - Zostało to naprawione w VS2015 i można to obejść w VS2013. Zobacz ten artykuł w Visual Studio Magazine .
Brian
-3

Czy jest dobry powód, aby preferować starsze, bardziej wyraźne style niż nowsze, bardziej zwięzłe?

Nie całkiem. Chociaż twierdziłbym, że za każdym razem, gdy masz taką własność, powinieneś po prostu skorzystać z pola publicznego.

Czy powinienem pisać nowe klasy w zwięzłym stylu, czy powinienem starać się dopasować starszy kod w celu zachowania spójności? Czy w tym przypadku spójność jest wystarczająca, aby uzasadnić starszy format?

Zakładając, że zignorowałeś powyższe rady i posiadasz właściwości przejściowe, nie chciałbym dopasować stylu. Idealnie byłoby wrócić i zmienić stary styl na nowy, aby był spójny, ale prawdopodobieństwo, że ludzie źle odczytają ten kod, jest niewielkie.

Telastyn
źródło
@svick - Bah. O ile nie zastanawiasz się nad typem, nie ma różnicy. Jeśli publicznie udostępniasz typ jako interfejs usługi lub biblioteki, ujawnisz interfejs, który i tak będzie wymagał właściwości. OP nie robi żadnej z tych rzeczy.
Telastyn
2
W tych ograniczonych przypadkach nie ma powodu technicznego . Nie zapominaj, że właściwości zachowują się inaczej w debuggerze, diagramach klas, intellisense, a gdy tylko myślisz, że nie robisz refleksji, jest .NET Framework. WinForms, WPF i kilka innych komponentów wewnętrznie używają odbicia i traktują właściwości osobno, więc porady dotyczące korzystania z pól publicznych będą słabo odbierane przez wielu deweloperów na tej stronie.
Kevin McCormick
1
@KevinMcCormick i framework zachowuje się doskonale (z tego co rozumiem) z polami. To naprawdę nie ma znaczenia, ponieważ są teraz automatyczne właściwości, ale większy problem polega na upublicznieniu tych rzeczy. Zdecydowanie zbyt wiele osób uważa, że ​​zwykłe uczynienie własności rzeczy w jakiś sposób zwalnia je z „nie robienia pól publicznych”.
Telastyn
2
Framework obsługuje je inaczej: spróbuj użyć pola publicznego w EF POCO, powiąż go z DataGridView, użyj PropertyGrid itp. Właśnie zrobiłem szybkie wyszukiwanie w moim dekompilatorze do Type.GetProperties / GetProperty, a Framework wywołuje tę metodę setki razy, ale nie GetField. Podsumowując, są one różne, a zalecenie, aby zawsze używać właściwości danych publicznych, opiera się częściowo na tym fakcie. Nie wyklucza to nigdy używania pól, ale ta sprawa wymaga uzasadnienia. Jednak twoja druga uwaga jest zdecydowanie prawdziwa, niedbałe ujawnianie danych publicznych jest zawsze złym pomysłem.
Kevin McCormick,