Найкращі практики щодо отримання, встановлення та властивостей. Java проти C #


92

Зараз я беру курс C # і намагаюся з’ясувати найкращий спосіб зробити це. Я походжу з мови Java, тому я знайомий лише з найкращими практиками Java; Я новачок на C #!

У Java, якщо я маю приватну власність, я роблю це;

private String name;

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

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

У C # я бачу, що існує багато способів зробити це.

Я можу це робити як Java:

private string name;

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

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

Або я можу зробити це так:

private string name;

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

Або:

public string Name { get; set; }

Який із них мені слід використовувати, і які застереження чи тонкощі пов’язані з кожним підходом? Створюючи класи, я дотримуюсь загальних передових практик, які я знаю з Java (особливо читання Effective Java). Так, наприклад, я виступаю за незмінність (надання сетерів лише за необхідності). Мені просто цікаво побачити, як ці практики поєднуються з різними способами надання сетерів та геттерів в C #; по суті, як би я переклав найкращі практики зі світу Java на C #?

РЕДАГУВАТИ

Я публікував це як коментар до відповіді Джона Скіта, але потім це затягнулося:

Що можна сказати про нетривіальну властивість (тобто, можливо, зі значною обробкою та підтвердженням)? Чи можу я все-таки виставити це через загальнодоступну власність, але з логікою, вкладеною в getі set? Чому я / повинен робити це, маючи спеціальні методи встановлення та отримання (із пов'язаною логікою обробки та перевірки).

Відповіді:


88

Pre-C # 6

Останній із них я б використав для тривіальної властивості. Зауважте, що я б назвав це загальнодоступною власністю, оскільки і гетери, і сетери є загальнодоступними.

Незмінність трохи болить при автоматично реалізованих властивостях - ви не можете написати автовластивість, яка має лише геттер; найближче, до якого ви можете підійти -

public string Foo { get; private set; }

який не є дійсно незмінні ... просто незмінні поза вашого класу. Тож ви можете замість цього використати справжню властивість лише для читання:

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

Ви точно не хочете писати getName()і setName(). У деяких випадках має сенс писати методи Get / Set, а не використовувати властивості, особливо якщо вони можуть бути дорогими, і ви хочете це підкреслити. Тим не менш, ви хотіли б дотримуватися конвенції іменування .NET PascalCase для методів, і ви не хотіли б, щоб тривіальна властивість, як це, у будь-якому випадку реалізовувалася за допомогою звичайних методів - властивість тут набагато ідіоматичніша.

C # 6

Ура, нарешті, у нас є належні автоматично реалізовані властивості лише для читання:

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

Точно так же тільки для читання властивості , які роблять необхідність робити якусь - то роботу, ви можете використовувати властивість елементів працездатні:

public double Area => height * width;

5
Точніше: будь-який перегляд коду вкаже, що спосіб Java - це хак, який обходить діючі конструкції середовища І (!) Виконання та вбиває використання властивості як властивості (тобто object.property = "value"). У деяких командах це призвело б до приємної розмови про ставлення - залежно від стажу в поєднанні зі стимулом застосовувати це ставлення до конкурента. Серйозно, НЕ БІЙСЯ МОВОЮ. Тим більше, що Java-шлях - це хак, який був обраний, щоб не модифікувати мову для підтримки реального майна.
TomTom

1
Думаю, хороша новина полягає в тому, що моя відповідь, здається, не суперечить усьому, що ви згадали. Погана новина - ваші пальці набагато швидші за мої. Велике розуміння та подяка за додані деталі.
jeremyalan

4
Щоб відповісти вам на редагування: ви можете використовувати методи get / set із будь-якою логікою в них, яку хочете. Ми часто робимо це, особливо для перевірки. Однак найкраща практика полягає у відсутності великої кількості повільної логіки (наприклад, доступ до бази даних), небезпечної логіки (створення винятків) або мутації (зміна великої кількості стану) у властивостях. Очікується, що майно буде діяти більш-менш як проста держава. Що завгодно більше, слід вказувати, використовуючи замість цього функцію.
CodexArcanum

2
@rtindru: Так, я це знаю. Цілком можливо написати властивість лише для читання. Але ви не можете оголосити автоматично реалізовану властивість без встановленого доступу.
Джон Скіт,


17

Якщо вам потрібна лише змінна для зберігання деяких даних:

public string Name { get; set; }

Хочете, щоб він відображався лише для читання?

public string Name { get; private set; }

Або ще краще ...

private readonly string _name;

...

public string Name { get { return _name; } }

Хочете перевірити вартість перед тим, як присвоювати майно?

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

      m_name = value;
   }
}

Загалом, GetXyz () та SetXyz () використовуються лише у певних випадках, і вам просто потрібно використовувати кишечник, коли він почуває себе добре. Загалом, я б сказав, що я очікую, що більшість властивостей get / set не містять багато логіки і мають дуже мало, якщо взагалі є, несподіваних побічних ефектів. Якщо для читання значення властивості потрібно викликати послугу або отримати вхідні дані від користувача, щоб побудувати об'єкт, який я запитую, тоді я б обернув його у метод і назвав би це приблизно так BuildXyz(), а не GetXyz().


2
Я б не викидав винятки у властивостях, я б скоріше використовував методи встановлення методів із контрактами для визначення такої специфічної поведінки. Якщо властивість має тип int, я б очікував, що кожен int відповідає вимогам. Щось, що вимагає метання винятків, на мій погляд, не є простим, за моїми викликами типу INotifyPropertyChanged більше в цьому рядку.
flindeberg

3
приватний сетер! = незмінний
piedar

piedar має рацію. Приватний установник просто означає, що я не можу виконувати призначення, але я все одно можу використовувати myList.Add(), наприклад (поки об'єкт піддається змінам, він може змінюватися).

1
@flindeberg Вибачте, але я не погоджуюсь ... А як щодо того, якщо у вас є індикатор прогресу із значенням Min/ Max. Ви хочете це переконатись Max > Min? Мати, SetRange(Min, Max)можливо, має сенс, але як тоді ви збираєтеся читати ці значення назад? Властивості Мін / Макс лише для читання? Видалення винятків для недійсного введення здається найчистішим способом вирішити це.
Basic

12

Використовуйте властивості в C #, а не методи get / set. Вони там для вашої зручності, і це ідіоматично.

Що стосується ваших двох прикладів на C #, то один - просто синтаксичний цукор для іншого. Використовуйте властивість auto, якщо вам потрібна проста обгортка навколо змінної екземпляра, використовуйте повну версію, коли вам потрібно додати логіку в геттер та / або сеттер.


5

У C # надайте перевагу властивостям виставляти приватні поля для get та / або set. Згадана вами форма - це автовластивість, де get and set автоматично генерує для вас приховане поле зворотної підтримки.

Я віддаю перевагу автоматичним властивостям, коли це можливо, але ви ніколи не повинні робити пару методів set / get у C #.


5
public string Name { get; set; }

Це просто автореалізована властивість , яка , і технічно така ж, як звичайна властивість. Під час компіляції буде створено поле підкладки.

Зрештою всі властивості перетворюються на функції, тому фактично скомпільована реалізація в кінцевому підсумку така ж, як ви звикли в Java.

Використовуйте автоматично реалізовані властивості, коли вам не потрібно виконувати певні операції в полі підкладки. В іншому випадку використовуйте звичайне майно. Використовуйте функції get та set, коли операція має побічні ефекти або є обчислювально дорогою, використовуйте властивості в іншому випадку.


4

Незалежно від того, який шлях ви вибрали в C #, кінцевий результат однаковий. Ви отримаєте змінну backinng з окремими методами getter та setter. Використовуючи властивості, ви дотримуєтесь найкращих практик, і тому справа в тому, наскільки багатослівно ви хочете отримати.

Особисто я б вибрав автоматичні властивості, остання версія:, public string Name { get; set; }оскільки вони займають найменшу кількість місця. І ви завжди можете розширити їх у майбутньому, якщо вам потрібно додати щось на зразок перевірки.


4

По можливості, я віддаю перевагу загальнодоступній, string Name { get; set; }оскільки вона лаконічна і легко читається. Однак бувають випадки, коли це потрібно

private string name;

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

2
Ви можете пояснити, коли і чому такі речі необхідні?
Jesper

Зараз я не можу придумати причину використовувати другу версію. Я лише сказав, що "це може бути часом, коли це необхідно". Я ненавиджу використовувати абсолюти, якщо я не позитивний.
SquidScare,

3
І чому це "lolz"? Ви можете показати свою підтримку коментаря, проголосувавши.
SquidScare,

1
Однією з причин використання явного поля підтримки є те, коли ви хочете виконати атомарні / потокобезпечні операції зі значенням
Basic

4

У C # кращий спосіб - це властивості, а не getX()і setX()методи. Також зауважте, що C # не вимагає, щоб властивості мали як get, так і набір - ви можете мати властивості get-only та set-only.

public boolean MyProperty
{
    get { return something; }
}

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

4

Спочатку дозвольте спробувати пояснити, що ви написали:

// 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; }
}

Ця структура також використовується сьогодні, але вона найбільш підходить, якщо ви хочете зробити додаткову функціональність, наприклад, коли встановлено значення, ви можете його проаналізувати, щоб зробити його великим і зберегти у приватному члені для зміни внутрішнього використання.

За допомогою .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
}

Бажаю тобі більше удачі в C #


3

Як зазначалося, усі ці підходи призводять до однакових результатів. Найголовніше, що ви обираєте конвенцію і дотримуєтесь її. Я вважаю за краще використовувати останні два приклади властивостей.


2

як і більшість відповідей тут, використовуйте автоматичні властивості. Інтуїтивно зрозумілий, менше рядків коду, і він більш чистий. Якщо вам слід серіалізувати свій клас, позначте клас [Serializable]/ [DataConract]атрибутом. А якщо ви використовуєте, [DataContract]позначте учасника за допомогою

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

Приватний або державний установник залежить від ваших уподобань.

Також зауважте, що для автоматичних властивостей потрібні як геттери, так і сеттери (загальнодоступні чи приватні).

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

Як варіант, якщо ви хочете лише отримати / встановити, створіть поле для підтримки самостійно


0

Який із них мені слід використовувати, і які застереження чи тонкощі пов’язані з кожним підходом?

При використанні властивостей є одне застереження, про яке ще не згадувалося: за допомогою властивостей ви не можете мати жодної параметризації ваших геттерів або сеттерів.

Наприклад, уявіть, що ви хочете отримати елементи списку і хочете одночасно застосувати фільтр. За допомогою методу get ви можете написати щось на зразок:

obj.getItems(filter);

На відміну від цього, з власністю ви змушені спочатку повернути всі предмети

obj.items

а потім застосувати фільтр на наступному кроці, або вам доведеться додати спеціальні властивості, які відображають елементи, відфільтровані за різними критеріями, що незабаром роздує ваш API:

obj.itemsFilteredByX
obj.itemsFilteredByY

Іноді може заважати, коли ви починали з властивості, наприклад, obj.itemsа потім пізніше виявили, що параметризація getter- або setter необхідна або полегшить роботу користувача API-класу. Тепер вам потрібно буде або переписати свій API, і змінити всі ті місця в коді, які отримують доступ до цієї властивості, або знайти альтернативне рішення. На відміну від цього, за допомогою методу get, наприклад obj.getItems(), ви можете просто розширити підпис методу, щоб прийняти необов’язковий об’єкт "конфігурації", наприкладobj.getItems(options) без необхідності переписувати всі ті місця, які викликають ваш метод.

При цьому властивості (автоматично реалізовані) в C # все ще є дуже корисними ярликами (з різних причин, згаданих тут), оскільки більша частина часу параметризації може не знадобитися - але це застереження існує.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.