Стоит ли создавать объекты в .NET?
Я только что переработал код коллеги, который, грубо говоря, выглядел так...
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 не вызывается из жесткого цикла, но широко используется во всем приложении.
Исправления
Я обновил примеры кода, так как ошибся с некоторыми параметрами.
Ответы
Ответ 1
Он может быть прав, если у вас есть миллионы этих объектов в памяти одновременно. Но если вы этого не сделаете, то он поднимает то, что почти наверняка спорный вопрос. Всегда сначала выбирайте лучший дизайн, а затем изменяйте его только в том случае, если вы не отвечаете требованиям к производительности.
Ответ 2
Создание нового объекта очень дешево. Вам нужно будет создать много объектов в очень узком цикле, чтобы заметить любую разницу... но в то же время это не бесплатно. Вы также должны иметь в виду, что затраты наполовину скрыты - у вас есть некоторая стоимость авансом при создании объекта, но также это означает, что сборщик мусора имеет больше работы.
Итак, более чистый дизайн стоит удара производительности? Мы не можем сказать вам, что это зависит от того, как используется ваш код. Я сильно подозреваю, что удар производительности будет незначительным, но если вы используете его в жесткой петле, это может и не быть. Там только один способ узнать: измерить это, если вы обеспокоены. Лично я бы, скорее всего, пошел на более чистый дизайн и только проверял производительность, когда казалось, что это становится проблемой.
(Если этот метод попадает на диск или базу данных, кстати, разница почти не будет незначительной.)
Одно предложение, кстати: действительно ли ваш тип HistoryEntry
должен быть изменчивым? Не могли бы вы сделать все свойства только для чтения, поддерживаемые частными переменными только для чтения?
Просто, чтобы упомянуть пункт, я несколько согласен с ним. Если эти два метода являются единственными местами, в которых вам нужна эта концепция, я вовсе не уверен, что для этого нужно создать новый тип. Не из-за аспекта производительности, а только потому, что вы вводите дополнительную кучу кода без каких-либо преимуществ. Здесь показано ключевое слово: если вы используете один и тот же набор параметров в других местах или можете с пользой поместить логику в класс HistoryEntry
, это совсем другое дело.
EDIT: просто чтобы ответить на вопрос о нескольких целых аргументах - С# 4 позволит использовать именованные аргументы, которые должны сделать это проще. Поэтому вы можете позвонить:
AddHistoryEntry(userId: 1, companyId: 10);
Чтобы имена, видимые с дополнительным классом, нуждались в одном из:
- Именованные аргументы конструктора из С# 4 (в этом случае вы не лучше, чем с помощью метода)
- Mutable types (urgh - это может привести к сложному отслеживанию ошибок, которые не были бы в исходной версии)
- Длинные статические имена методов для создания экземпляров ( "FromUserAndCompanyId" ), которые становятся громоздкими с большим количеством параметров
- Тип изменяемого построителя
- Куча методов "С":
new HistoryEntry().WithCompanyId(...).WithUserId(...)
- это усложняет проверку во время компиляции, когда вы предоставили все необходимые значения.
Ответ 3
Почему не статический метод в самом классе?
public static void AddHistoryEntry(HistoryEntry entry)
Сохраняет класс утилиты. Новый объект может быть дорогостоящим, он может и не быть - но это, вероятно, не имеет значения. Хорошие значения по умолчанию позволяют значительно расширить код, и вы сохраните больше времени на обслуживание, чем в производительности, если только вы не делаете этого в цикле.
Ответ 4
Вы оба ошибаетесь, но он более не прав, чем вы.
Ответ 5
Это дорого по сравнению с не созданием объектов в .NET, так же, как 1 - большое число относительно 0. Все зависит от вашей перспективы. Если это в цикле, который запускается 20 миллионов раз, я могу волноваться; в противном случае мне было бы все равно.
Сказав это, я хотел бы указать, что Utility
не является хорошим именем для класса, а рефакторинг в HistoryEntry
также, вероятно, требует проверки того, что передаваемый параметр не является null
, требование, которое вы не имели первоначально, когда все параметры были типами значений.
Также обычно считается, что анти-шаблон имеет объекты с данными, но не поведение, которое HistoryEntry
похоже. Старайтесь избегать этого слишком часто.
Ответ 6
Не очень дорого создавать объекты в .NET. На самом деле создание объектов происходит очень быстро, даже если вы создаете миллионы из них.
Однако сбор мусора не из дешевых. Если вы создаете много недолговечных объектов, вы можете столкнуться со значительным замедлением, когда gc начнет работать. Имеет смысл избежать создания новых объектов с коротким периодом, если создание выполняется тысячи или миллионы раз. Если, например, вы создаете несколько объектов с каждым сетевым пакетом, принятым сервером с тысячами подключений.
Тем не менее, никогда не следует избегать вызова new
, прежде чем демонстрировать, что многие объекты действительно замедляют gc. В вашем примере они, скорее всего, не захотят.
Ответ 7
Это действительно узкое место в производительности? Если нет, тогда было бы явным примером преждевременной оптимизации, чтобы избежать использования объектов в этом случае. Поддержание работоспособности и дизайн должны иметь приоритет над производительностью, пока вы не начнете сталкиваться с серьезными проблемами с производительностью.
Ответ 8
Это немного дороже вычислительно дорого, но для этой цели вы получаете преимущество более чистого, более концептуального кода, который может быстрее реагировать на будущие бизнес-требования. Производительность и маневренность.
Ответ 9
Нет. Сборщик мусора оптимизирован для обеспечения высокой эффективности при работе с недолговечными объектами.