Почему не рекомендуется иметь свойство set-only?

9

Сегодня на работе один из моих коллег просмотрел мой код и предложил удалить свойство «только для набора» и использовать вместо него метод.

Поскольку мы оба были заняты другими делами, он сказал мне взглянуть на Property Designраздел из книги «Руководство по разработке структуры». В книге писатель просто сказал, чтобы избежать:

Свойства с установщиком, имеющим более широкий доступ, чем получатель

И теперь мне интересно, почему не рекомендуется иметь свойство set-only? Может кто-нибудь уточнить для меня?

Прашант Чолачагудда
источник
6
Можете ли вы описать ситуацию, в которой, по вашему мнению, подходящим было только свойство для набора? Это может сделать ответы немного более актуальными.
JohnFx
1
Я пытаюсь придумать пример, который имеет смысл семантически. Единственное, что приходит на ум - это Passwordсвойство Userкласса. Вы можете установить это, но вы не можете получить это. Тогда вы могли бы иметь HashedPasswordсвойство только для чтения . Вызов набора сделал бы хэш и изменил бы HashedPasswordсвойство. Я бы не стал кричать на тебя, если бы ты сделал это.
Скотт Уитлок

Ответы:

15

Я думаю, что это может иметь отношение к ожиданиям. Свойства только для наборов встречаются редко, и свойства обычно используются для «тупых» наборов просто для хранения значения без особой обработки. Если вы выполняете большую работу в сеттере, лучше использовать метод - люди ожидают, что выполнение методов может занять много времени и иметь побочные эффекты. Реализация подобного типа поведения в свойстве может привести к коду, который нарушает ожидания.

Вот соответствующий раздел Руководства Microsoft по использованию собственности :

Свойства против методов

Разработчикам библиотек классов часто приходится выбирать между реализацией члена класса как свойства или метода. В общем, методы представляют действия, а свойства представляют данные. Используйте следующие рекомендации, чтобы помочь вам выбрать один из этих вариантов.

  • Используйте свойство, когда элемент является логическим элементом данных. В следующих объявлениях членов 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

Потому что это просто не имеет смысла в большинстве случаев. Какое свойство вы можете иметь, которое вы можете установить, но не прочитать?

Если ОО предназначен для лучшего представления реального мира, свойство «только набор» может указывать на то, что ваше моделирование не в порядке.

Изменить : См. Также: /programming/4564928/are-set-only-properties-bad-practice, который по существу говорит, что это не интуитивно понятно, а свойство только для набора является в основном методом с другим именем, поэтому вы должны использовать метод.

Джон Хопкинс
источник
1
Я использовал свойства только для набора ранее. Они пишут в приватное поле объекта, чтобы настроить его поведение. Они полезны, когда внешнему коду не нужно знать текущее значение, но, возможно, потребуется изменить его. Это редко, конечно, но я видел, как это случилось.
Мейсон Уилер
@ Мейсон - я бы никогда не сказал, что вы никогда не должны их использовать, но они должны быть скорее исключением, чем правилом.
Джон Хопкинс
@MasonWheeler не так ли приблизительно Foo Foo { private get; set; }? Я бы не назвал это только записью
Калет
6

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

Использование метода вместо свойства только для набора будет немного менее запутанным для пользователя. Имя метода , как правило , указывает на комплект- или get- , но имена свойств обычно не указывают , что что - то может только быть установлен и не быть получены. Я предполагаю, что если бы свойство было чем-то вроде «ReadOnlyBackgroundColour», это не могло бы сбить с толку других кодеров, но это выглядело бы странно.

FrustratedWithFormsDesigner
источник
Я согласен, но как будет отличаться метод установки в этом случае?
Трэвис Кристиан
1
@ Travis Christian: Похоже, что OP работает в ситуации, когда есть сеттер, но нет геттера. Поэтому они могут что-то установить, но никогда не узнают, изменится ли это позже.
FrustratedWithFormsDesigner
@ Разочарованный Но они также никогда не узнают, если что-то снова изменилось с помощью метода.
Адам Лир
@ Анна Лир ♦: Если есть геттер, вы можете по крайней мере проверить значение перед его использованием, если у вас есть точка в коде, где заданное вами значение может внезапно подвергнуться сомнению.
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используются хранимая процедура и массив значений по умолчанию SqlParameter, каждый из которых имеет соответствующее свойство 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 являются классами, а не примитивными значениями, свойства WriteOnly позволяют упростить интерфейс для настройки параметров.

Так что это мои мысли.

Могу ли я вместо этого преобразовать его в метод? конечно, но интерфейс кажется ... менее приятным

  R2.BudgetID = SomeBudgetID

против

  R2.SetBudgetID(SomeBudgetID)
fnostro
источник