Чому не рекомендується мати властивість лише для встановлення?


9

Сьогодні на роботі один з моїх колег переглянув мій код і запропонував я видалити властивість лише набору та використовувати метод замість цього.

Оскільки ми обидва були зайняті іншими справами, він сказав мені переглянути Property Designрозділ із книги "Настанови рамкового дизайну". У книзі письменник просто сказав уникати:

Властивості, у яких сеттер має ширший доступ, ніж геттер

І тепер мені цікаво, чому не рекомендується мати властивість лише для встановлення? Може хтось уточнить для мене?


6
Чи можете ви описати ситуацію, коли ви вважали, що властивість лише для набору є доречною? Це може зробити відповіді трохи більш актуальними.
JohnFx

1
Я намагаюся придумати приклад, який має сенс семантично. Єдине, що спадає на думку - це Passwordвластивість Userкласу. Ви можете встановити його, але ви не можете його отримати. Тоді ви можете мати HashedPasswordвластивість лише для читання . Виклик набору зробить хеш і змінить HashedPasswordвластивість. Я б не кричав на тебе, якби ти це зробив.
Скотт Вітлок

Відповіді:


15

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

Ось відповідний розділ Посібника з використання майна Майкрософт :

Властивості проти методів

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

  • Використовуйте властивість, коли член є членом логічних даних. У наступних деклараціях учасника Nameє властивістю, оскільки це логічний член класу.
public string Name
{
    get 
    {
        return name;
    }
    set 
    {
        name = value;
    }
}

Використовуйте метод, коли:

  • Операція - це перетворення, наприклад Object.ToString.
  • Операція досить дорога, що потрібно повідомити користувачеві, що він повинен розглянути можливість кешування результату.
  • Отримання значення властивості за допомогою getаксесуара мало б помітний побічний ефект.
  • Виклик члена два рази поспіль дає різні результати.
  • Порядок виконання важливий. Зауважте, що властивості типу слід мати можливість встановлювати та отримувати в будь-якому порядку.
  • Член є статичним, але повертає значення, яке можна змінити.
  • Член повертає масив. Властивості, що повертають масиви, можуть бути дуже оманливими. Зазвичай необхідно повернути копію внутрішнього масиву, щоб користувач не міг змінити внутрішній стан. Це в поєднанні з тим, що користувач може легко припустити, що це властивість індексований, призводить до неефективного коду. У наступному прикладі коду кожен виклик властивості Methods створює копію масиву. В результаті буде створено 2 ^ n + 1 копії масиву в наступному циклі.
Type type = // Get a type.
for (int i = 0; i < type.Methods.Length; i++)
{
   if (type.Methods[i].Name.Equals ("text"))
   {
      // Perform some operation.
   }
}

[... пропустив довший приклад ...]

Властивості лише для читання та лише для запису

Ви повинні використовувати властивість лише для читання, коли користувач не може змінити логічний член даних властивості. Не використовуйте властивості лише для запису.


Так - тут діє принцип найменшого сюрпризу.
Пол Різник

6

Тому що це просто не має сенсу в більшості випадків. Яку властивість ви можете встановити, але не можете прочитати?

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

Редагувати : Див. Також: /programming/4564928/are-set-only-properties-bad-practice, який, по суті, говорить, що це неінтуїтивність, а властивість лише набору - це в основному метод іншої назви, тому ви повинні використовувати метод.


1
Раніше я використовував властивості лише набору. Вони записують у приватне поле об'єкта, щоб налаштувати його поведінку. Вони корисні, коли зовнішньому коду не потрібно знати поточне значення, але може знадобитися його змінити. Це звичайно, звичайно, але я бачив, як це відбувається.
Мейсон Уілер

@ Мейсон - Я, звичайно, ніколи не пітиму так, щоб сказати, що ви ніколи не використовуєте їх, але вони, по суті, повинні бути винятком, а не правилом.
Джон Хопкінс

@MasonWheeler - це не так Foo Foo { private get; set; }? Я б не закликав це писати
Калет

6

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

Використання методу замість властивості лише набору буде дещо менш заплутаною для користувача. Ім'я методу , як правило , вказує на Комплект- або get- , але імена властивостей зазвичай не вказують , що що - то може тільки бути встановлений і НЕ бути отримані. Я гадаю, якби властивість була чимось на зразок "ReadOnlyBackgroundColour", це не заплутало б інших кодерів, але це виглядало б просто дивно.


Я погоджуюся, але чим же метод сеттера може відрізнятися в цьому випадку?
Тревіс Крістіан

1
@Travis Christian: Здається, що ОП працює з ситуацією, коли є сетер, але немає геттера. Тож вони можуть щось встановити, але вони ніколи не дізнаються, чи зміниться це згодом.
FrustratedWithFormsDesigner

@Frustrated Але вони також ніколи не дізнаються, чи знову щось змінили методом.
Адам Лір

@Anna Lear ♦: Якщо є геттер, ви можете принаймні протестувати значення, перш ніж використовувати його, якщо у вас є точка в коді, де значення, яке ви встановили, може раптом виникнути сумніви.
FrustratedWithFormsDesigner

3
@ Розчарований я згоден. Просто питання полягало у використанні властивості лише набір проти використання методу зробити те саме.
Адам Лір

-1

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

У мене є набір ActiveReportкласів, які є частиною веб-сайту, який створений за допомогою екземпляра та працює за допомогою постбекбека після декількох виборів користувачів.

Код VB виглядає приблизно так:

  Public Class SomeReport
    Private greader As New GenericReporting.CommonReader("AStoredProcedure", 
                                      {New SqlParameter("budget_id", 0)})

    Public WriteOnly Property BudgetID As Integer
      Set(value As Integer)
        greader.Parameters("budget_id").Value = value
      End Set
    End Property

    Public Sub New(Optional budget_id As Integer = 0)
      ' This call is required by the designer.
      InitializeComponent()

      ' Add any initialization after the InitializeComponent() call.
      BudgetID = budget_id
    End Sub
  End Class

Ці звіти використовують загальні кишки, CommonReaderзаймає збережену процедуру і масив SqlParameters за замовчуванням , кожен з яких має пов'язане властивість WriteOnly, яке, в залежності від дизайну звіту, може бути передано як параметр для інстанції або встановлено користувачем після інстанції до виклик Runметоду звітів .

  '''''''''''''''''''''''
  ' Parameter taken from a user selected row of a GridView
  '
  Dim SomeBudgetID As Integer = gvBudgets.SelectedDataKey.Values(budget_id)

  '''''''''''''''''''''''      
  ' On Instantiation
  '
  Dim R as ActiveReport = New SomeReport(SomeBudgetID)
  R.Run()

  '''''''''''''''''''''''      
  ' Or On Instantiation using "With" syntax
  '
  Dim R as ActiveReport = New SomeReport() With {.BudgetID = SomeBudgetID}
  R.Run()

  '''''''''''''''''''''''
  ' Or After
  '
  Dim R as ActiveReport = New SomeReport()
  R.BudgetID = SomeBudgetID
  R.Run()

Отже, як я бачу, маючи в цьому випадку властивість лише для запису

  1. Дозволяє більш сильно перевіряти тип, оскільки SqlParameters є загальними
  2. Більшу гнучкість у створенні звіту можна звітувати негайно, якщо всі параметри будуть доступні або додані потім, коли вони стануть доступними.
  3. Властивості підтримують синтаксис "З" при створенні інстанцій
  4. Чи дійсно необхідний "гетьтер", оскільки параметри відомі користувачеві та не змінюються у Звіті?
  5. Оскільки SqlParameters є класами, а не примітивними значеннями, Properties Properties властивості WriteOnly дозволяють створити більш простий інтерфейс для встановлення параметрів

Так це мої думки.

Чи можу я замість цього перетворити його на метод? Впевнений, але інтерфейс здається ... менш приємним

  R2.BudgetID = SomeBudgetID

проти

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