Преимущество типов значений перед ссылочными типами? - PullRequest
6 голосов
/ 19 февраля 2012

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

Через некоторое время меня поразило, что, хотя я вижу дефицит использования типов значений, я не знал ни о каких преимуществах.
Так что мой вопрос довольно прост - какова цель иметь типы значений? что мы получаем, копируя структуру, а не просто создавая новую ссылку на нее?

Мне кажется, было бы намного проще иметь только ссылочные типы, как в Java.

Редактировать: Просто чтобы прояснить это, я имею в виду не типы значений, меньшие 8 байт (максимальный размер ссылки), а скорее типы значений, которые имеют 8 байтов или более.

Например, структура Rectangle, содержащая четыре значения int.

Ответы [ 6 ]

13 голосов
/ 19 февраля 2012
  • Экземпляр однобайтового типа значения занимает один байт.Тип ссылки занимает место для ссылки, плюс блок синхронизации и таблицу виртуальных функций и ...

  • Чтобы скопировать ссылку, вы копируете четырех (или восемь) байтовую ссылку,Чтобы скопировать четырехбайтовое целое число, вы копируете четырехбайтовое целое число.Копирование типов небольших значений не дороже, чем копирование ссылок.

  • Типы значений, которые не содержат ссылок, вообще не должны проверяться сборщиком мусора.Каждая ссылка должна отслеживаться сборщиком мусора.

3 голосов
/ 19 февраля 2012

Типы значений обычно более производительны, чем ссылочные типы:

  • Тип ссылки стоит дополнительной памяти для справки и производительности при разыменовании

  • Тип значения не требует дополнительной сборки мусора.Он собирает мусор вместе с экземпляром, в котором он находится. Локальные переменные в методах очищаются при выходе из метода.

  • Массивы типов значений эффективны в сочетании с кэшами.(Подумайте о массиве целых по сравнению с массивом экземпляров типа Integer)

3 голосов
/ 19 февраля 2012

«Создание ссылки» не проблема.Это просто копия 32/64 бит.Создание объекта - это то, что стоит дорого.На самом деле создание объекта - это дешево, а собирать - нет.

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

Редактировать: Эрик Липперт привел замечательный пример в комментариях: «Сколько байтов занимает массив из одного миллиона байтов, если они значениятипы? Сколько это займет, если они являются ссылочными типами? "

Я отвечу: если для структуры упаковки установлено значение 1, такой массив займет 1 миллион 16 байт (в 32-битной системе).Для использования ссылочных типов потребуется:

array, object header: 12
array, length: 4
array, data: 4*(1 million) = 4m
1 million objects, headers = 12 * (1 million)
1 million objects, data padded to 4 bytes: 4 * (1 million)

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

2 голосов
/ 19 февраля 2012

Усиление видно, если ваши данные маленькие (<16 байт), у вас много экземпляров и / или вы много манипулируете ими, особенно передавая функции. Это связано с тем, что создание объекта является относительно дорогим по сравнению с созданием экземпляра типа малого значения. И как кто-то еще отметил, объекты должны быть собраны, и это еще дороже. Кроме того, типы с очень маленькими значениями занимают меньше памяти, чем их эквиваленты ссылочного типа. </p>

Примером не примитивного типа значения в .NET является структура Point (System.Drawing).

1 голос
/ 06 июня 2012

Одним из основных преимуществ типов значений, таких как Rectangle, является то, что если у вас есть n хранилищ типа Rectangle, можно быть уверенным, что у каждого есть n различных экземпляроввведите Rectangle.Если у одного есть массив MyArray типа Rectangle, длиной не менее двух, оператор, подобный MyArray[0] = MyArray[1], скопирует поля MyArray[1] в поля MyArray[0], но они будут продолжать ссылаться на отдельные Rectangle экземпляров.Если затем выполнить строку оператора MyArray[0].X += 4, которая изменит поле X одного экземпляра без изменения значения X любого другого слота массива или экземпляра Rectangle.Кстати, обратите внимание, что при создании массива он мгновенно заполняется доступными для записи Rectangle экземплярами.

Представьте себе, если Rectangle был изменяемым типом класса.Создание массива изменяемых Rectangle экземпляров потребовало бы, чтобы одно первое измерение массива, а затем назначил каждому элементу в массиве новый Rectangle экземпляр.Если бы кто-то хотел скопировать значение одного экземпляра прямоугольника в другой, ему нужно было бы сказать что-то вроде MyArray[0].CopyValuesFrom(MyArray[1]) [что, конечно, не сработало бы, если бы MyArray[0] не было заполнено ссылкой на новый экземпляр).Если кто-то случайно скажет MyArray[0] = MyArray[1], то запись в MyArray[0].X также повлияет на MyArray[1].X.Противные вещи.

Важно отметить, что в C # и vb.net есть несколько мест, где компилятор неявно копирует тип значения, а затем действует на копию, как если бы это был оригинал.Это действительно неудачный языковой дизайн, который побудил некоторых людей выдвинуть предположение, что типы значений должны быть неизменяемыми (поскольку большинство ситуаций, связанных с неявным копированием, вызывают проблемы только с изменяемыми типами значений).Когда компиляторы очень плохо предупреждали о случаях, когда семантически сомнительные копии приводили к нарушению поведения, такое представление могло быть разумным.Однако сегодня его следует считать устаревшим, учитывая, что любой приличный современный компилятор будет отмечать ошибки в большинстве сценариев, где неявное копирование приведет к нарушению семантики, включая все сценарии, в которых структуры изменяются только с помощью конструкторов, установщиков свойств или внешних назначений общедоступным изменяемым полям.,Выражение типа MyArray[0].X += 5 гораздо более читабельно, чем MyArray[0] = new Rectangle(MyArray[0].X + 5, MyArray[0].Y, MyArray[0].Width, MyArray[0].Height).

1 голос
/ 19 февраля 2012

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

Типы значений (Struct) содержат свои данные, размещенные в стеке, или встроенные в структуре.Типы ссылок (Class) хранят ссылку на адрес памяти значения и размещаются в куче.

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

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

что мы получаем, копируя структуру, а не просто создавая новую ссылку на нее?

Если вы копируете структуру, C # создает новую копиюобъект и назначает копию объекта отдельному экземпляру структуры.Однако, если вы копируете класс, C # создает новую копию ссылки на объект и назначает копию ссылки на отдельный экземпляр класса.Структуры не могут иметь деструкторов, но классы могут иметь деструкторы.

...