Najlepsze praktyki dotyczące metod pobierających, ustawiających i właściwości. Java kontra C #


92

Chodzę teraz na zajęcia z C # i próbuję znaleźć najlepszy sposób robienia rzeczy. Pochodzę z języka Java, więc znam tylko najlepsze praktyki dotyczące języka Java; Jestem nowicjuszem C #!

W Javie, jeśli mam prywatną własność, robię to;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

W języku C # widzę, że jest na to wiele sposobów.

Mogę to zrobić jak Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Albo mogę to zrobić w ten sposób:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Lub:

public string Name { get; set; }

Którego należy użyć i jakie są zastrzeżenia lub subtelności związane z każdym podejściem? Tworząc zajęcia kieruję się ogólnymi dobrymi praktykami, które znam z języka Java (zwłaszcza czytając efektywną Javę). Na przykład opowiadam się za niezmiennością (zapewnianie ustawiaczy tylko wtedy, gdy jest to konieczne). Jestem po prostu ciekawy, jak te praktyki pasują do różnych sposobów dostarczania metod ustawiających i pobierających w języku C #; zasadniczo, jak przełożyłbym najlepsze praktyki ze świata Java na C #?

EDYTOWAĆ

Publikowałem to jako komentarz do odpowiedzi Jona Skeeta, ale potem zrobiło się to długo:

A co z nietrywialną właściwością (tj., Być może z dużym przetwarzaniem i walidacją)? Czy nadal mogę go ujawnić za pośrednictwem własności publicznej, ale z logiką zawartą w geti set? Dlaczego miałbym / powinienem robić to ponad mając dedykowane metody ustawiające i pobierające (z powiązaną logiką przetwarzania i walidacji).

Odpowiedzi:


88

Pre-C # 6

Ostatniego z nich użyłbym do trywialnej własności. Zauważ, że nazwałbym to własnością publiczną, ponieważ zarówno metody pobierające, jak i ustawiające są publiczne.

Niezmienność jest trochę uciążliwa z automatycznie zaimplementowanymi właściwościami - nie można napisać właściwości automatycznej, która ma tylko metodę pobierającą; najbliżej możesz przyjść to:

public string Foo { get; private set; }

co nie jest niezmienne ... tylko niezmienne poza twoją klasą. Więc możesz zamiast tego użyć prawdziwej właściwości tylko do odczytu:

private readonly string foo;
public string Foo { get { return foo; } }

Na pewno nie chcesz pisać getName()i setName(). W niektórych przypadkach sensowne jest pisanie metod Get / Set zamiast używania właściwości, szczególnie jeśli mogą one być drogie i chcesz to podkreślić. Jednak chciałbyś przestrzegać konwencji nazewnictwa .NET w PascalCase dla metod i nie chciałbyś, aby taka trywialna właściwość została zaimplementowana w normalnych metodach - właściwość jest tutaj znacznie bardziej idiomatyczna.

C # 6

Hurra, w końcu mamy automatycznie zaimplementowane właściwości tylko do odczytu:

// This can only be assigned to within the constructor
public string Foo { get; }

Podobnie dla właściwości tylko do odczytu, które zrobić trzeba popracować, można użyć właściwości członków treściwe:

public double Area => height * width;

5
Dokładniej: każdy przegląd kodu wskaże, że sposób java jest hackem, który omija prawidłowe konstrukcje środowiska wykonawczego langauge AND (!) I zabija użycie właściwości jako proeprty (tj. Object.property = "value"). W niektórych zespołach zaowocowałoby to miłą rozmową na temat postawy - w zależności od stażu pracy połączonej z zachętą do zastosowania tego nastawienia u konkurenta. Poważnie, NIE WALCZ Z LANGAUGE. Zwłaszcza, że ​​java "droga" to hack, który został wybrany, aby nie modyfikować języka dla obsługi nieruchomości.
TomTom,

1
Myślę, że dobrą wiadomością jest to, że moja odpowiedź nie wydaje się zaprzeczać niczego, o czym wspomniałeś. Zła wiadomość jest taka, że ​​twoje palce są znacznie szybsze niż moje. Świetny wgląd i dzięki za dodatkowe szczegóły.
jeremyalan

4
Odpowiadając, edytuj: możesz użyć metod get / set z dowolną ilością logiki, jaką chcesz. Często to robimy, zwłaszcza w celu walidacji. Najlepszą praktyką jest jednak unikanie powolnej logiki (na przykład dostępu do bazy danych), niebezpiecznej logiki (zgłaszanie wyjątków) lub mutacji (zmienianie wielu stanów) we właściwościach. Oczekuje się, że właściwość będzie działać mniej więcej jak zwykły stan. Coś więcej należy wskazać za pomocą funkcji.
CodexArcanum,

2
@rtindru: Tak, jestem tego świadomy. Całkiem możliwe jest napisanie właściwości tylko do odczytu. Ale nie można zadeklarować właściwości zaimplementowanej automatycznie bez metody dostępu set.
Jon Skeet,

2
Od C # 6 można teraz mieć właściwości automatyczne bez metody dostępu set ( public int Y { get; } = y;).
Scott Chamberlain

17

Jeśli potrzebujesz tylko zmiennej do przechowywania niektórych danych:

public string Name { get; set; }

Chcesz, aby był on tylko do odczytu?

public string Name { get; private set; }

Albo jeszcze lepiej ...

private readonly string _name;

...

public string Name { get { return _name; } }

Chcesz sprawdzić wartość przed przypisaniem właściwości?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Ogólnie rzecz biorąc, GetXyz () i SetXyz () są używane tylko w niektórych przypadkach i po prostu musisz użyć swojego jelita, gdy czujesz się dobrze. Ogólnie rzecz biorąc, spodziewam się, że większość właściwości get / set nie będzie zawierała dużej ilości logiki i będzie miała bardzo mało, jeśli w ogóle, nieoczekiwanych skutków ubocznych. Jeśli odczytanie wartości właściwości wymaga wywołania usługi lub uzyskania danych wejściowych od użytkownika w celu zbudowania obiektu, o który żądam, to zawinąłbym go w metodę i nazwałbym to czymś podobnym BuildXyz(), a nie GetXyz().


2
Nie rzucałbym wyjątków we właściwościach, wolałbym raczej używać seterów metod z kontraktami, aby określić takie konkretne zachowanie. Jeśli właściwość jest typu int, spodziewałbym się, że każda int się kwalifikuje. Coś, co wywołuje wyrzucanie wyjątków, nie jest moim zdaniem proste , według mnie wywołania typu INotifyPropertyChanged są bardziej w tym wierszu.
flindeberg

3
seter prywatny! = niezmienny
piedar

piedar ma rację. Ustawiający prywatny oznacza po prostu, że nie mogę przypisywać zadań, ale mogę nadal używać myList.Add(), na przykład (dopóki obiekt jest narażony na zmianę, jest zmienny).

1
@flindeberg Przepraszam, ale nie zgadzam się ... A jeśli masz pasek postępu z wartością Min/ Max. Chcesz to zapewnić Max > Min? Posiadanie SetRange(Min, Max)makijaż może sens, ale to jak masz zamiar czytać te wartości z powrotem? Właściwości Min / Max tylko do odczytu? Zgłaszanie wyjątków dla nieprawidłowych danych wejściowych wydaje się najczystszym sposobem rozwiązania tego problemu.
Podstawowy

12

Użyj właściwości w języku C #, a nie metod pobierania / ustawiania. Są tam dla Twojej wygody i jest to idiomatyczne.

Jeśli chodzi o twoje dwa przykłady w C #, jeden jest po prostu cukrem syntaktycznym dla drugiego. Użyj właściwości auto, jeśli potrzebujesz tylko prostego opakowania wokół zmiennej instancji, użyj pełnej wersji, gdy potrzebujesz dodać logikę do metody pobierającej i / lub ustawiającej.


5

W języku C # preferują właściwości do ujawniania pól prywatnych do pobierania i / lub ustawiania. Ta forma, o której wspomniałeś, to autopertyza, w której get and set automatycznie generuje ukryte pole bazowe przestawne.

Preferuję właściwości automatyczne, jeśli to możliwe, ale nigdy nie powinieneś robić pary metod set / get w C #.


5
public string Name { get; set; }

Jest to po prostu właściwość zaimplementowana automatycznie i jest technicznie taka sama jak normalna właściwość. Podczas kompilacji zostanie utworzone pole zapasowe.

Wszystkie właściwości są ostatecznie konwertowane na funkcje, więc ostatecznie skompilowana implementacja jest taka sama, jak w Javie.

Użyj właściwości implementowanych automatycznie, gdy nie musisz wykonywać określonych operacji na polu zapasowym. W przeciwnym razie użyj zwykłej właściwości. Użyj funkcji get i set, gdy operacja ma skutki uboczne lub jest kosztowna obliczeniowo, w przeciwnym razie użyj właściwości.


4

Niezależnie od tego, który sposób wybierzesz w C #, efekt końcowy jest taki sam. Otrzymasz zmienną backinng z oddzielnymi metodami pobierającymi i ustawiającymi. Korzystając z właściwości, postępujesz zgodnie z najlepszymi praktykami, więc jest to kwestia tego, jak dokładny chcesz uzyskać.

Osobiście wybrałbym właściwości automatyczne, ostatnią wersję: public string Name { get; set; }ponieważ zajmują najmniej miejsca. I zawsze możesz je rozszerzyć w przyszłości, jeśli potrzebujesz czegoś takiego jak walidacja.


4

Jeśli to możliwe, wolę publiczne, string Name { get; set; }ponieważ jest zwięzłe i łatwe do odczytania. Jednak może się zdarzyć, że będzie to konieczne

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
czy możesz wyjaśnić, kiedy i dlaczego takie działania są konieczne?
Jesper

W tej chwili nie mogę wymyślić powodu, dla którego miałbym korzystać z drugiej wersji. Powiedziałem tylko „może być czas, kiedy będzie to konieczne”. Nienawidzę używać absolutów, chyba że jestem pewien.
SquidScareMe

3
A dlaczego to „lolz”? Możesz wyrazić swoje poparcie dla komentarza, oddając głos za.
SquidScareMe

1
Jednym z powodów, dla których warto użyć jawnego pola zapasowego, jest sytuacja, gdy chcesz wykonać niepodzielne / bezpieczne dla wątków operacje na wartości
Podstawowy

4

C # preferowanym sposobem jest przez właściwości aniżeli getX()i setX()Metody. Należy również zauważyć, że C # nie wymaga, aby właściwości miały zarówno get, jak i set - możesz mieć właściwości tylko do pobierania i właściwości tylko do zestawu.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

Najpierw spróbuję wyjaśnić, co napisałeś:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Ta struktura jest również używana w dzisiejszych czasach, ale jest najbardziej odpowiednia, jeśli chcesz wykonać dodatkowe funkcje, na przykład gdy ustawiono wartość, możesz ją przeanalizować, aby ją wykorzystać i zapisać w prywatnym elemencie w celu zmiany użytku wewnętrznego.

Dzięki .NET Framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Życzę powodzenia w C #


3

Jak wspomniano, wszystkie te podejścia prowadzą do tego samego wyniku. Najważniejsze, żeby wybrać konwencję i się jej trzymać. Wolę używać dwóch ostatnich przykładów właściwości.


2

podobnie jak większość odpowiedzi tutaj, użyj właściwości automatycznych. Intuicyjny, mniej linijek kodu i bardziej przejrzysty. Jeśli powinieneś serializować swoją klasę, oznacz klasę [Serializable]/ [DataConract]atrybutem. A jeśli używasz, [DataContract]oznacz członka za pomocą

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Ustawiacz prywatny lub publiczny zależy od twoich preferencji.

Należy również zauważyć, że właściwości automatyczne wymagają zarówno metod pobierających, jak i ustawiających (publicznych lub prywatnych).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Alternatywnie, jeśli chcesz tylko pobrać / ustawić, samodzielnie utwórz pole zapasowe


0

Którego należy użyć i jakie są zastrzeżenia lub subtelności związane z każdym podejściem?

W przypadku właściwości istnieje jedno zastrzeżenie, o którym jeszcze nie wspomniano: w przypadku właściwości nie można mieć żadnej parametryzacji metod pobierających ani ustawiających.

Na przykład wyobraź sobie, że chcesz pobrać elementy listy i jednocześnie chcesz zastosować filtr. Za pomocą metody get możesz napisać coś takiego:

obj.getItems(filter);

W przeciwieństwie do właściwości, musisz najpierw zwrócić wszystkie elementy

obj.items

a następnie zastosuj filtr w następnym kroku lub musisz dodać dedykowane właściwości, które eksponują elementy przefiltrowane według różnych kryteriów, co wkrótce nadyma Twój interfejs API:

obj.itemsFilteredByX
obj.itemsFilteredByY

To, co czasami może być uciążliwe, to sytuacja, w której zaczynasz od właściwości, np. obj.itemsA później odkrywasz, że parametryzacja gettera lub settera jest potrzebna lub może ułatwić pracę użytkownikowi klasowego API. Będziesz teraz musiał albo przepisać swoje API i zmodyfikować wszystkie te miejsca w kodzie, które mają dostęp do tej właściwości, albo znaleźć alternatywne rozwiązanie. W przeciwieństwie do metody get, np. obj.getItems()Możesz po prostu rozszerzyć sygnaturę swojej metody, aby zaakceptowała opcjonalny obiekt "konfiguracyjny", np. obj.getItems(options)Bez konieczności przepisywania wszystkich miejsc, które wywołują twoją metodę.

Biorąc to pod uwagę, (automatycznie implementowane) właściwości w C # są nadal bardzo przydatnymi skrótami (z różnych powodów wymienionych tutaj), ponieważ przez większość czasu parametryzacja może nie być potrzebna - ale to zastrzeżenie pozostaje.

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.