Является ли создание объектов в геттерах плохой практикой? - PullRequest
25 голосов
/ 20 января 2010

Давайте создадим объект в геттере следующим образом:

public class Class1
{
       public string Id { get; set; }
       public string Oz { get; set; }
       public string Poznamka { get; set; }

       public Object object
       {
             get
             {
                  // maybe some more code
                  return new Object { Id = Id, poznamla = Poznamka, Oz = OZ };
             }
        }
 }

Или мне лучше создать метод, который будет создавать и возвращать объект?

Ответы [ 14 ]

22 голосов
/ 20 января 2010

Да, это плохая практика.

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

16 голосов
/ 20 января 2010

Свойства выглядят как поля, но они являются методами. Это, как было известно, вызывает феноменальное количество беспорядка. Когда программист видит код, который, по-видимому, обращается к полю, программист делает множество предположений, которые могут быть неверными для свойства. Итак, существуют некоторые общие рекомендации по проектированию свойств.

  1. Избегайте возврата разных значений из свойства get. Если метод вызывается несколько раз подряд, метод свойства может каждый раз возвращать другое значение; поле возвращает одно и то же значение каждый раз.

  2. Метод свойства может потребовать дополнительной памяти или вернуть ссылку на что-то, что на самом деле не является частью состояния объекта, поэтому изменение возвращаемого объекта не влияет на исходный объект; запрос поля всегда возвращает ссылку на объект, который гарантированно является частью состояния исходного объекта. Работа со свойством, которое возвращает копию, может быть очень запутанной для разработчиков, и эта характеристика часто не документируется.

  3. Учтите, что свойство не может быть передано в качестве параметра out или ref методу; поле может.

  4. Избегайте длительных методов получения свойства. Метод свойства может занять много времени для выполнения; Доступ к полю всегда завершается немедленно.

  5. Избегайте выдачи исключений из получателей.

  6. Сохранять предыдущие значения, если установщик свойств генерирует исключение

  7. Избегайте видимых побочных эффектов.

  8. Разрешить установку свойств в любом порядке, даже если это приводит к временному недопустимому состоянию объектов.

Источники

" CLR via C # ", Джеффри Рихтер. Глава 9. Интеллектуальное определение свойств

" Руководство по проектированию рамок " 2-е издание, Брэд Абрамс, Кшиштоф Квалина, глава 5.2 Проектирование собственности

8 голосов
/ 20 января 2010

Если вы хотите, чтобы ваш получатель создавал новый объект каждый раз, когда к нему обращаются, это способ сделать это. Этот шаблон обычно называется фабричным методом .

Однако обычно это не требуется для свойств (т. Е. Методов получения и установки), и поэтому считается плохой практикой.

4 голосов
/ 20 января 2010

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

при чтении дважды из поля или свойства вы ожидаете две вещи:

  • не влияет на поведение (внешнее) объекта
  • вы получите идентичные результаты

У меня нет реальных знаний о C #, но я надеюсь, что следующее разъясняет мою точку зрения. давайте начнем так:

Object o1 = myInst.object;
Object o2 = myInst.object;
o1.poznamka = "some note";

в случае поля будут выполнены следующие условия:

o1 == o2;
o2.poznamka == "some note";

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

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

2 голосов
/ 20 января 2010

По моему мнению, если что-то является 'свойством', получатель должен вернуть вам свойство (в основном данные, которые уже существуют), относящиеся к объекту.

В вашем случае вы возвращаете что-то, что не является собственностью этого объекта в данный момент. Вы не возвращаете свойство своего объекта, а продукт какого-либо действия.

Я бы вместо этого использовал метод, похожий на GetMyObject (). Особенно, если произойдет «действие», я думаю, что в большинстве случаев лучше иметь метод, чем свойство name .

И попытайтесь представить, чего ожидают другие разработчики, не знакомые с вашим кодом, увидев вашу собственность.

2 голосов
/ 20 января 2010

Собственность должна, во всех отношениях, действовать как поле. Это означает, что никакие исключения не должны создаваться, и никакие новые объекты не должны создаваться (поэтому вы не создаете много ненужных объектов, если свойство используется в цикле)

Вместо этого используйте класс-оболочку или аналогичный.

1 голос
/ 21 января 2010

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

У меня был класс с

private Collection<int> moo;

public Collection<int> Moo
{
  get 
  {
    if (this.moo == null) this.moo = new Collection<int>();
    return this.moo;
  }
}

Тогда где-то еще в классе был публичный метод, который ссылался на

this.moo.Add(baa);

без проверки был создан экземпляр.

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

Некоторое время я должен признать, что думал, что схожу с ума. Отладчик - без ошибок. Ошибка выполнения. Чуть позже почесав голову, я заметил ошибку и понял, что отладчик Visual Studio создает экземпляр Collection, поскольку он отображает открытые свойства класса.

1 голос
/ 20 января 2010

Для написания кода, который легко тестируется, необходимо поддерживать разделение инициализации объекта.

То есть, пока в тестовых случаях у вас нет тестов для некоторых определенных предметов.

как и в объекте "Дом", вы не хотите проверять что-либо, связанное с кухонным объектом. и ты хочешь проверить только сад. поэтому, пока вы инициируете домашний класс и инициируете объект в некоторых конструкторах или в геттерах, вы не будете хорошо кодировать, что будет поддерживать тестирование.

1 голос
/ 20 января 2010

Свойство - это просто удобный способ выразить вычисляемое поле.

Он все равно должен представлять что-то о объекте, независимо от того, как получено само значение. Например, если рассматриваемый объект представляет собой счет-фактуру, вам, возможно, придется сложить стоимость каждой отдельной позиции и вернуть итоговую сумму.

То, что написано в вопросе, нарушает это правило, потому что возвращение копии объекта не является чем-то, что описывает объект. Если возвращаемое значение изменяется между вызовами свойства без явного изменения состояния объекта , тогда объектная модель нарушается.

Если говорить в общих чертах, то возвращение нового объекта, подобного этому, почти всегда нарушает правило (я не могу сейчас представить контрпример), поэтому я бы сказал, что это плохая практика.

Существует также множество свойств, где вы можете так легко и невинно вызывать свойство несколько раз и в конечном итоге запустить один и тот же код (который, надеюсь, не медленный!).

0 голосов
/ 20 января 2010

Это плохая практика. Но если вы думаете об объектах как о группе получателей и установщиков, вам следует проверить классические дискуссии на эту тему.

Как отмечали некоторые люди, ленивая загрузка может быть причиной для этого. Зависит от фактической бизнес-логики, которую вы моделируете здесь. Вы должны создать отдельный метод, если он лучше для удобочитаемости, но если код для создания объекта прост, вы можете избежать косвенного обращения.

...