Структуры против классов - PullRequest
       5

Структуры против классов

87 голосов
/ 15 октября 2010

Я собираюсь создать 100 000 объектов в коде.Они маленькие, только с 2 или 3 свойствами.Я помещу их в общий список, и когда они появятся, я зациклю их и проверим значение a и, возможно, обновлю значение b.

Быстрее / лучше создавать эти объекты как классили как структура?

РЕДАКТИРОВАТЬ

a.Свойства являются типами значений (кроме строки, я думаю?)

b.Они могут (пока мы не уверены) иметь метод проверки

РЕДАКТИРОВАТЬ 2

Мне было интересно: объекты в куче и стеке обрабатываются одинаковосборщик мусора, или это работает по-другому?

Ответы [ 10 ]

126 голосов
/ 15 октября 2010

Это быстрее , чтобы создать эти объекты как класс или как структуру?

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

Структуры потребляют меньше динамической памяти (потому что они меньше и легче уплотняются, а не потому, что они "в стеке"). Но они копируют больше времени, чем эталонная копия. Я не знаю, каковы ваши показатели производительности для использования памяти или скорости; здесь есть компромисс, и вы тот человек, который знает, что это такое.

лучше создавать эти объекты как класс или как структуру?

Может быть, класс, может быть структура. Как правило большого пальца: Если объект:
1. Маленький
2. Логически неизменное значение
3. Их много
Тогда я бы подумал сделать это структурой. В противном случае я бы придерживался ссылочного типа.

Если вам нужно изменить какое-то поле структуры, обычно лучше создать конструктор, который возвращает всю новую структуру с правильно заданным полем. Возможно, это немного медленнее (измерить это!), Но логически гораздо проще рассуждать.

Объекты в куче и стеке обрабатываются сборщиком мусора одинаково?

Нет , они не совпадают, потому что объекты в стеке являются корнями коллекции . Сборщику мусора не нужно спрашивать "жива ли эта штука в стеке?" потому что ответ на этот вопрос всегда "Да, он в стеке". (Теперь вы не можете полагаться на это, чтобы поддерживать объект живым, потому что стек является деталью реализации. Джиттер позволяет вносить оптимизации, которые, скажем, регистрируют то, что обычно было бы значением стека, и тогда он никогда не будет в стеке, поэтому сборщик мусора не знает, что он все еще жив. Зарегистрированные объекты могут агрессивно собирать своих потомков, как только удерживающий его регистр не будет снова считываться.)

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

Это ясно?

22 голосов
/ 15 октября 2010

Иногда с struct вам не нужно вызывать конструктор new () и напрямую назначать поля, делая его намного быстрее, чем обычно.

Пример:

Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i].id = i;
    list[i].is_valid = true;
}

примерно в 2-3 раза быстрее, чем

Value[] list = new Value[N];
for (int i = 0; i < N; i++)
{
    list[i] = new Value(i, true);
}

, где Value - это struct с двумя полями (id и is_valid).

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

7 голосов
/ 15 октября 2010

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

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

Подробнее здесь:

http://msdn.microsoft.com/en-us/library/aa288471(VS.71).aspx

6 голосов
/ 15 октября 2010

Массивы структур представлены в куче в непрерывном блоке памяти, в то время как массив объектов представлен в виде непрерывного блока ссылок с фактическими объектами в других местах в куче, что требует памяти как для объектов, так и для ссылки на их массивы.

В этом случае, когда вы помещаете их в List<>List<> резервируется в массив), было бы более эффективно использовать структуры в памяти.

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

4 голосов
/ 15 октября 2010

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

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

Edit:

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

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

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

3 голосов
/ 15 октября 2010

Структура в своей основе - не что иное, как совокупность полей.В .NET структура может «притворяться» объектом, и для каждого типа структуры .NET неявно определяет тип объекта кучи с теми же полями и методами, которые, будучи объектом кучи, будут вести себя как объект,Переменная, которая содержит ссылку на такой объект кучи («штучная» структура), будет демонстрировать семантику ссылок, но та, которая содержит структуру напрямую, является просто совокупностью переменных.

Я думаю, что большая часть структуры противПутаница в классе связана с тем фактом, что структуры имеют два очень разных варианта использования, которые должны иметь очень разные руководящие принципы проектирования, но руководящие принципы MS не различают их.Иногда нужно что-то, что ведет себя как объект;в этом случае рекомендации MS довольно разумны, хотя «предел в 16 байтов», вероятно, должен быть больше похож на 24–32.Иногда, однако, необходимо объединение переменных.Структура, используемая для этой цели, должна просто состоять из нескольких открытых полей и, возможно, переопределения Equals, переопределения ToString и реализации IEquatable(itsType).Equals.Структуры, которые используются как совокупности полей, не являются объектами и не должны притворяться ими.С точки зрения структуры, значение поля должно быть не чем иным, как «последним, что написано в этом поле».Любое дополнительное значение должно определяться клиентским кодом.

Например, если структура, агрегирующая переменную, имеет члены Minimum и Maximum, сама структура не должна давать обещание, что Minimum <= Maximum.Код, который получает такую ​​структуру в качестве параметра, должен вести себя так, как если бы ему были переданы отдельные значения Minimum и Maximum.Требование, чтобы Minimum не превышало Maximum, должно рассматриваться как требование, чтобы параметр Minimum не превышал отдельно передаваемый Maximum.

Полезный шаблон, который иногда нужно рассмотреть, - это определить класс ExposedHolder<T>, например:

class ExposedHolder<T>
{
  public T Value;
  ExposedHolder() { }
  ExposedHolder(T val) { Value = T; }
}

Если у вас есть List<ExposedHolder<someStruct>>, где someStruct - агрегатная переменнаяstruct, можно делать что-то вроде myList[3].Value.someField += 7;, но передача myList[3].Value другому коду даст ему содержимое Value, а не средство его изменения.Напротив, если бы вы использовали List<someStruct>, было бы необходимо использовать var temp=myList[3]; temp.someField += 7; myList[3] = temp;.Если бы кто-то использовал изменяемый тип класса, то раскрытие содержимого myList[3] для внешнего кода потребовало бы копирования всех полей в какой-либо другой объект.Если бы кто-то использовал тип неизменяемого класса или структуру «объектного стиля», было бы необходимо создать новый экземпляр, который был бы похож на myList[3], за исключением someField, который отличался, и затем сохранить этот новый экземпляр в списке.

Еще одно примечание: если вы храните большое количество похожих вещей, может быть целесообразно хранить их в возможно вложенных массивах структур, предпочтительно пытаясь сохранить размер каждого массива в диапазоне от 1 КБ до 64 КБ.или так.Массивы структур являются особыми, так как при индексировании можно получить прямую ссылку на структуру внутри, так что можно сказать «a [12] .x = 5;».Хотя можно определять объекты, подобные массиву, C # не позволяет им использовать такой синтаксис для массивов.

3 голосов
/ 15 октября 2010

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

1 голос
/ 15 октября 2010

Что ж, если вы используете struct afterall, избавьтесь от строки и используйте символьный или фиксированный буфер фиксированного размера.

Это re: performance.

1 голос
/ 15 октября 2010

С точки зрения c ++ я согласен с тем, что модифицировать свойства структуры будет медленнее, чем в классе.Но я думаю, что они будут читать быстрее из-за структуры, размещаемой в стеке, а не в куче.Чтение данных из кучи требует больше проверок, чем из стека.

1 голос
/ 15 октября 2010

Используйте классы.

На заметку общего характера.Почему бы не обновить значение b при их создании?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...