Дорого ли создавать объекты в .Net? - PullRequest
13 голосов
/ 21 января 2010

Я только что переработал код коллеги, который примерно выглядел примерно так ...

public class Utility
  public void AddHistoryEntry(int userID, HistoryType Historytype, int companyID)
  {
    // Do something...
  }
  public void AddHistoryEntry(int userID, HistoryType historyType, int companyID, string notes)
  {
    // Do something...
  }
}

К этому ...

public class HistoryEntry
{
  public long UserID { get; private set; }
  public HistoryType HistoryType { get; private set; }
  public long CompanyID { get; set; }
  public string Notes { get; set; }

  public HistoryEntry(long userID, HistoryType historyType)
  {
    this.UserID = userID;
    this.HistoryType = historyType;
  }
}

public class Utility
{
  public void AddHistoryEntry(HistoryEntry entry)
  {
    // Do something...
  }
}

}

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

Он прав?

Дополнительные пояснения

Спасибо за все ответы до сих пор. Я упустил много деталей в надежде краткости, но некоторые из них вызвали проблемы с ответами.

  • Класс утилит не существует. Я просто хотел поместить методы в класс, чтобы вы все могли их увидеть. На самом деле это разумный класс.
  • На самом деле в исходном коде было 5 методов AddHistoryEntry. Все это заняло много int-параметров. Одной из причин рефакторинга было то, что AddHistory(0, 1, 45, 3); мало что говорит вам!
  • AddHistoryEntry не вызывается из узкого цикла, но широко используется в приложении.

Исправления

Я обновил примеры кода, поскольку допустил ошибку с некоторыми параметрами.

Ответы [ 9 ]

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

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

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

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

Так стоит ли более чистый дизайн производительности? Мы не можем вам этого сказать - это зависит от того, как используется ваш код. Я сильно подозреваю, что снижение производительности будет незначительным, но если вы используете это в узком цикле, это может быть не так. Есть только один способ выяснить это: измерить, если вы обеспокоены. Лично я бы, наверное, просто выбрал более чистый дизайн и проверял производительность только тогда, когда казалось, что это становится проблемой.

(Кстати, если этот метод работает с диском или базой данных, разница почти привязана к незначительной.)

Кстати, одно предложение: действительно ли ваш тип HistoryEntry должен быть изменчивым? Не могли бы вы сделать все свойства доступными только для чтения, подкрепленными личными переменными только для чтения?

Просто упомянуть точку зрения Уилла - я с ним несколько согласен. Если эти два метода являются только местами, где вам нужна эта концепция, я вовсе не уверен, что ради нее стоит создать новый тип. Не из-за аспекта производительности, а просто потому, что вы вводите дополнительный пакет кода без какой-либо очевидной выгоды. Здесь демонстрируется ключевое слово - если вы на самом деле используете тот же набор параметров в других местах или могли бы с пользой поместить логику в класс HistoryEntry, это совсем другое дело.

РЕДАКТИРОВАТЬ: Просто чтобы ответить на вопрос о нескольких целочисленных аргументах - C # 4 позволит именованные аргументы, которые должны сделать это проще. Чтобы вы могли позвонить:

AddHistoryEntry(userId: 1, companyId: 10);

Чтобы сделать имена видимыми с помощью дополнительного класса, необходимо:

  • Именованные аргументы конструктора из C # 4 (в этом случае вам не лучше, чем с помощью метода)
  • Изменяемые типы (urgh - это может привести к трудным отслеживанию ошибок, которых не было бы в оригинальной версии)
  • Длинные статические имена методов для создания экземпляров ("FromUserAndCompanyId"), которые становятся громоздкими с большим количеством параметров
  • Изменяемый тип конструктора
  • Набор методов «С»: new HistoryEntry().WithCompanyId(...).WithUserId(...) - это затрудняет проверку во время компиляции того, что вы предоставили все необходимые значения.
5 голосов
/ 21 января 2010

Почему не статический метод в самом классе?

public static void AddHistoryEntry(HistoryEntry entry)

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

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

Это дорого по сравнению с , а не созданием объектов в .NET, точно так же, как 1 - большое число по отношению к 0. Все зависит от вашей перспективы. Если это будет цикл, который запускается 20 миллионов раз, я мог бы волноваться; иначе мне было бы все равно.

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

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

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

Вы оба не правы, но он более неправ, чем вы.

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

Создание объектов в .NET не очень дорого. Фактически, создание объектов очень быстро, даже если вы создаете миллионы из них.

Однако сборка мусора стоит не дешево. Если вы создаете много недолговечных объектов, вы можете ощутить значительное замедление при запуске gc. Имеет смысл избегать создания новых недолговечных объектов, если создание выполняется тысячи или миллионы раз. Например, если вы создаете несколько объектов с каждым сетевым пакетом, полученным сервером с тысячами соединений.

Тем не менее, никогда не стоит избегать вызова new до того, как продемонстрирует, что множество объектов действительно делают замедление gc. В вашем примере они, скорее всего, не будут.

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

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

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

Нет.Сборщик мусора оптимизирован для обеспечения высокой эффективности при работе с недолговечными объектами.

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

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

...