Когда вы должны использовать структуру, а не класс в C #? Моя концептуальная модель состоит в том, что структуры используются во времена, когда элемент представляет собой просто набор типов значений . Способ логически объединить их все в единое целое.
Я столкнулся с этими правилами здесь :
- Структура должна представлять одно значение.
- Структура должна иметь объем памяти менее 16 байт.
- Структура не должна быть изменена после создания.
Эти правила работают? Что означает семантически структура?
System.Drawing.Rectangle
нарушает все три этих правила.Ответы:
Источник, на который ссылается OP, заслуживает доверия ... но как насчет Microsoft - какова позиция по использованию структуры? Я искал дополнительное обучение у Microsoft , и вот что я нашел:
Microsoft постоянно нарушает эти правила
Хорошо, № 2 и № 3 в любом случае. Наш любимый словарь имеет 2 внутренних структуры:
* Справочный источник
Источник 'JonnyCantCode.com' получил 3 из 4 - вполне простительно, поскольку № 4, вероятно, не будет проблемой. Если вы обнаружите, что занимаетесь структурой, переосмыслите свою архитектуру.
Давайте посмотрим, почему Microsoft будет использовать эти структуры:
Entry
иEnumerator
представляет отдельные значения.Entry
никогда не передается как параметр вне класса Dictionary. Дальнейшие исследования показывают, что для удовлетворения реализации IEnumerable словарь используетEnumerator
структуру, которую он копирует каждый раз при запросе перечислителя ... имеет смысл.Enumerator
является общедоступным, поскольку Dictionary является перечислимым и должен иметь равный доступ к реализации интерфейса IEnumerator, например, к получателю IEnumerator.Обновление. Кроме того, следует понимать, что когда структура реализует интерфейс - как это делает Enumerator - и приводится к этому реализованному типу, структура становится ссылочным типом и перемещается в кучу. Внутренний по отношению к классу Dictionary, Enumerator по- прежнему является типом значения. Однако, как только метод вызывает
GetEnumerator()
, ссылочный типIEnumerator
возвращается .Здесь мы не видим каких-либо попыток или доказательств необходимости сохранять неизменяемыми структуры или поддерживать размер экземпляра не более 16 байт:
readonly
- не является неизменнымEntry
имеет неопределенный срок службы (отAdd()
, доRemove()
,Clear()
или мусора);И ... 4. Обе структуры хранят TKey и TValue, которые, как мы все знаем, вполне могут быть ссылочными типами (дополнительная информация о бонусе)
Несмотря на хешированные ключи, словари работают быстро, потому что создание структуры быстрее, чем ссылочного типа. Здесь у меня есть,
Dictionary<int, int>
который хранит 300 000 случайных целых чисел с последовательно увеличенными ключами.Вместимость : количество элементов, доступных до изменения размера внутреннего массива.
Memsize : определяется путем сериализации словаря в MemoryStream и получения длины в байтах (достаточно точной для наших целей).
Завершено Изменение размера : время, необходимое для изменения размера внутреннего массива с 150862 элементов до 312874 элементов. Когда вы понимаете, что каждый элемент последовательно копируется через
Array.CopyTo()
, это не так уж и плохо.Общее время для заполнения : по общему признанию искажено из-за регистрации и
OnResize
события, которое я добавил к источнику; Тем не менее, по-прежнему впечатляет заполнить 300 тысяч целых чисел при изменении размера в 15 раз во время операции Просто из любопытства, сколько будет общего времени, чтобы заполнить, если бы я уже знал емкость? 13 мсИтак, что теперь, если бы
Entry
был класс? Будут ли эти времена или показатели действительно сильно отличаться?Очевидно, большая разница заключается в изменении размера. Есть ли разница, если словарь инициализируется с Capacity? Не достаточно, чтобы беспокоиться о ... 12 мс .
Что происходит, потому что
Entry
это структура, она не требует инициализации, как ссылочный тип. Это и красота, и проклятие типа значения. Чтобы использоватьEntry
в качестве ссылочного типа, мне нужно было вставить следующий код:Причину, по которой мне нужно было инициализировать каждый элемент массива
Entry
в качестве ссылочного типа, можно найти в MSDN: Конструкция структуры . Короче говоря:Это на самом деле довольно просто , и мы будем брать из Азимова три закона робототехники :
... что мы уберем от этого : короче говоря, будем нести ответственность за использование типов значений. Они бывают быстрыми и эффективными, но имеют способность вызывать много неожиданных действий, если не будут должным образом поддерживаться (например, непреднамеренные копии).
источник
Decimal
илиDateTime
], то, если она не будет соблюдать другие три правила, она должна быть заменена классом. Если структура содержит фиксированный набор переменных, каждая из которых может содержать любое значение, которое было бы допустимо для ее типа [напримерRectangle
], тогда она должна соблюдать другие правила, некоторые из которых противоречат правилам для структур с «одним значением» ,Dictionary
тип записи на основании того, что это только внутренний тип, производительность считается более важной, чем семантика, или какое-то другое оправдание. Моя точка зрения заключается в том, что типу типаRectangle
должно быть предоставлено его содержимое как редактируемые по отдельности поля не "потому что" преимущества производительности перевешивают возникающие семантические недостатки, а потому что тип семантически представляет фиксированный набор независимых значений , и поэтому изменяемая структура более производительный и семантически превосходящий .Всякий раз, когда вам:
Однако предостережение заключается в том, что структуры (произвольно большие) обходятся дороже, чем ссылки на классы (обычно одно машинное слово), поэтому классы могут оказаться быстрее на практике.
источник
(Guid)null
(допустимо приводить ноль к ссылочному типу), среди прочего.Я не согласен с правилами, приведенными в оригинальном посте. Вот мои правила:
1) Вы используете структуры для производительности при хранении в массивах. (см. также, Когда составляется ответ? )
2) Они нужны вам при передаче кода структурированных данных в / из C / C ++
3) Не используйте структуры, если они вам не нужны:
источник
struct
чтобы знать, как оно будет себя вести, но если что-тоstruct
с открытыми полями, это все, что нужно знать. Если объект предоставляет свойство типа открытого поля-структуры, и если код считывает эту структуру в переменную и изменяет ее, можно с уверенностью предсказать, что такое действие не повлияет на объект, свойство которого было прочитано, до тех пор, пока структура не будет записана обратно. Напротив, если бы свойство было изменяемым типом класса, его чтение и изменение могло бы обновить базовый объект, как и ожидалось, но ...Используйте структуру, когда вам нужна семантика значений, а не ссылочная семантика.
редактировать
Не уверен, почему люди опровергают это, но это верный момент, и он был сделан до того, как операционная служба прояснила его вопрос, и это самая фундаментальная основная причина для структуры.
Если вам нужна ссылочная семантика, вам нужен класс, а не структура.
источник
В дополнение к ответу «это значение», один конкретный сценарий использования структур - это когда вы знаете, что у вас есть набор данных, вызывающий проблемы со сборкой мусора, и у вас много объектов. Например, большой список / массив экземпляров Person. Естественной метафорой здесь является класс, но если у вас есть большое количество долгоживущих экземпляров Person, они могут в конечном итоге засорить GEN-2 и вызвать остановку GC. Если варранты сценарии, один из возможных подходов здесь использовать массив (не список) Person структура , то есть
Person[]
. Теперь, вместо миллионов объектов в GEN-2, у вас есть отдельный кусок в LOH (я предполагаю, что здесь нет строк и т. Д., Т.е. чистое значение без каких-либо ссылок). Это имеет очень небольшое влияние GC.Работать с этими данными неудобно, поскольку данные, вероятно, слишком велики для структуры, и вы не хотите постоянно копировать жирные значения. Однако доступ к нему напрямую в массиве не копирует структуру - она на месте (в отличие от индексатора списка, который копирует). Это означает большую работу с индексами:
Обратите внимание, что поддержание неизменности самих значений поможет здесь. Для более сложной логики используйте метод с параметром by-ref:
Опять же, это на месте - мы не скопировали значение.
В очень специфических сценариях эта тактика может быть очень успешной; тем не менее, это довольно продвинутый сценарий, который следует использовать, только если вы знаете, что делаете и почему. По умолчанию здесь будет класс.
источник
List
Я полагаю, используетArray
закулисные. нет?Из спецификации языка C # :
Альтернатива - сделать Point структурой.
Теперь создается только один объект - один для массива - и экземпляры Point хранятся в виде строки в массиве.
Конструкторы Struct вызываются оператором new, но это не означает, что память выделяется. Вместо того, чтобы динамически размещать объект и возвращать ссылку на него, конструктор структуры просто возвращает само значение структуры (обычно во временном месте в стеке), и это значение затем копируется по мере необходимости.
В случае классов две переменные могут ссылаться на один и тот же объект и, таким образом, операции с одной переменной могут влиять на объект, на который ссылается другая переменная. В случае структур каждая переменная имеет свою собственную копию данных, и операции с одним из них не могут повлиять на другой. Например, вывод, полученный следующим фрагментом кода, зависит от того, является ли Point классом или структурой.
Если Point является классом, результат равен 20, потому что a и b ссылаются на один и тот же объект. Если Point является структурой, выходное значение равно 10, поскольку при присваивании a to b создается копия значения, и на эту копию не влияет последующее присвоение ax
В предыдущем примере освещены два ограничения структур. Во-первых, копирование всей структуры обычно менее эффективно, чем копирование ссылки на объект, поэтому присвоение и передача параметров-значений могут быть более дорогими для структур, чем для ссылочных типов. Во-вторых, за исключением параметров ref и out, невозможно создавать ссылки на структуры, что исключает их использование в ряде ситуаций.
источник
ref
изменяемой структуре и знать, что любые мутации, которые внешний метод выполнит с ним, будут выполнены до его возврата. Это очень плохо. Net не имеет никакого понятия о эфемерных параметрах и возвращаемых значениях функций, поскольку ...ref
с помощью объектов классов. По сути, локальные переменные, параметры и возвращаемые значения функций могут быть постоянными (по умолчанию), возвратными или эфемерными. Код будет запрещено копировать эфемерные вещи во что-либо, что переживет нынешнюю сферу. Возвращаемые вещи будут похожи на эфемерные, за исключением того, что они могут быть возвращены из функции. Возвращаемое значение функции будет связано с самыми жесткими ограничениями, применимыми к любому из его «возвращаемых» параметров.Структуры хороши для атомарного представления данных, где указанные данные могут быть скопированы несколько раз кодом. Клонирование объекта, как правило, обходится дороже, чем копирование структуры, поскольку включает в себя выделение памяти, запуск конструктора и удаление / удаление мусора, когда это делается.
источник
Вот основное правило.
Если все поля-члены являются типами значений, создайте структуру .
Если какое-либо одно поле-член является ссылочным типом, создайте класс . Это связано с тем, что поле ссылочного типа в любом случае будет нуждаться в выделении кучи.
Exmaples
источник
string
семантически эквивалентным значениям, и сохранение ссылки на неизменяемый объект в поле не влечет за собой выделения кучи. Разница между структурой с открытыми общественными полями и объектом класса с открытыми общественными полями, что , учитывая последовательность кодаvar q=p; p.X=4; q.X=5;
,p.X
будет иметь значение 4 , еслиa
это тип структуры, и 5 , если это тип класса. Если кто-то хочет иметь возможность легко модифицировать элементы типа, следует выбрать «класс» или «структуру» в зависимости от того, хотят ли измененияq
повлиятьp
.ArraySegment<T>
инкапсулирует aT[]
, который всегда является типом класса. Тип структурыKeyValuePair<TKey,TValue>
часто используется с типами классов в качестве общих параметров.Первое: взаимодействие сценариев или когда вам нужно указать расположение памяти
Второе: когда данные в любом случае имеют почти такой же размер, что и ссылочный указатель.
источник
Вам необходимо использовать "struct" в ситуациях, когда вы хотите явно указать макет памяти с помощью StructLayoutAttribute - обычно для PInvoke.
Редактировать: Комментарий указывает, что вы можете использовать класс или структуру со StructLayoutAttribute, и это, безусловно, верно. На практике вы обычно используете struct - она распределяется в стеке, а не в куче, что имеет смысл, если вы просто передаете аргумент в вызов неуправляемого метода.
источник
Я использую структуры для упаковки или распаковки любого двоичного формата связи. Это включает чтение или запись на диск, списки вершин DirectX, сетевые протоколы или работу с зашифрованными / сжатыми данными.
Три указанных вами руководства не были полезны для меня в этом контексте. Когда мне нужно выписать четыреста байтов материала в определенном порядке, я определю структуру размером четыреста байтов и заполню ее любыми несвязанными значениями, которые она должна иметь, и я собираюсь установить его любым способом, который имеет больше всего смысла в то время. (Хорошо, четыреста байтов было бы довольно странно - но когда я писал для жизни файлы Excel, я имел дело со структурами размером до сорока байтов всего, потому что это то, насколько велики некоторые записи BIFF.)
источник
За исключением типов значений, которые используются непосредственно средой выполнения и различными другими для целей PInvoke, значения типов типов следует использовать только в 2 сценариях.
источник
this
параметра, используемого для вызова его методов); классы позволяют дублировать ссылки..NET поддерживает
value types
иreference types
(в Java вы можете определять только ссылочные типы). Экземплярыreference types
get выделяются в управляемой куче и собираются, когда на них нет ожидающих ссылок. Экземплярыvalue types
, с другой стороны, выделяются вstack
, и, следовательно, выделенная память восстанавливается, как только заканчивается их область действия. И, конечно же,value types
передать по значению, иreference types
по ссылке. Все примитивные типы данных C #, за исключением System.String, являются типами значений.Когда использовать структуру над классом,
В C #
structs
естьvalue types
классыreference types
. Вы можете создавать типы значений в C #, используяenum
ключевое слово иstruct
ключевое слово. Использованиеvalue type
вместо areference type
приведет к уменьшению количества объектов в управляемой куче, что приведет к меньшей нагрузке на сборщик мусора (GC), менее частым циклам GC и, следовательно, к повышению производительности. Впрочем,value types
есть и свои минусы тоже. Передача большого числаstruct
определенно обходится дороже, чем передача ссылки, это одна очевидная проблема. Другая проблема связана с накладными расходамиboxing/unboxing
. Если вам интересно, чтоboxing/unboxing
означает, перейдите по этим ссылкам для хорошего объясненияboxing
иunboxing
. Помимо производительности, бывают случаи, когда вам просто нужно, чтобы типы имели семантику значений, что было бы очень сложно (или уродливо) реализовать, еслиreference types
все, что у вас есть. Вы должны использоватьvalue types
только, когда вам нужна семантика копирования или требуется автоматическая инициализация, обычно вarrays
этих типах.источник
ref
. Передача любой структуры размера поref
затратам аналогична передаче ссылки на класс по значению. Копирование структуры любого размера или передача по значению дешевле, чем выполнение защитной копии объекта класса и сохранение или передача ссылки на него. Классы больших времен лучше, чем структуры для хранения значений: (1) когда классы неизменны (чтобы избежать защитного копирования), и каждый созданный экземпляр будет много передаваться, или ...readOnlyStruct.someMember = 5;
- не создаватьsomeMember
свойство только для чтения, а вместо этого сделать его полем.Структура является типом значения. Если вы присвоите структуру новой переменной, новая переменная будет содержать копию оригинала.
Исключение следующих результатов приводит к 5 экземплярам структуры, хранящейся в памяти:
Класс является ссылочным типом. Когда вы назначаете класс новой переменной, переменная содержит ссылку на исходный объект класса.
Исключение следующих результатов приводит только к одному экземпляру объекта класса в памяти.
Struct s может увеличить вероятность ошибки кода. Если объект значения обрабатывается как изменяемый ссылочный объект, разработчик может удивиться, когда сделанные изменения будут неожиданно потеряны.
источник
Я сделал небольшой тест с BenchmarkDotNet чтобы лучше понять преимущества "struct" в числах. Я тестирую циклический просмотр массива (или списка) структур (или классов). Создание этих массивов или списков выходит за рамки теста - ясно, что более тяжелый «класс» будет использовать больше памяти и будет включать GC.
Итак, вывод таков: будьте осторожны с LINQ и боксом / распаковкой скрытых структур, а использование структур для микрооптимизации строго придерживается массивов.
PS Есть еще один тест для прохождения struct / class через стек вызовов https://stackoverflow.com/a/47864451/506147
Код:
источник
Структурные типы в C # или других языках .net обычно используются для хранения вещей, которые должны вести себя как группы значений фиксированного размера. Полезный аспект типов структуры состоит в том, что поля экземпляра типа структуры могут быть изменены путем изменения места хранения, в котором он хранится, и никаким другим способом. Можно закодировать структуру таким образом, что единственный способ изменить любое поле - это создать совершенно новый экземпляр, а затем использовать присвоение структуры, чтобы изменить все поля цели, перезаписав их значениями из нового экземпляра, но если структура не предоставляет средств для создания экземпляра, где ее поля имеют значения не по умолчанию, все ее поля будут изменяемыми, если и если сама структура хранится в изменяемой папке.
Обратите внимание, что можно спроектировать тип структуры так, чтобы он по существу вел себя как тип класса, если структура содержит частное поле типа класса и перенаправляет своих собственных членов на объект обернутого класса. Например, a
PersonCollection
может предлагать свойстваSortedByName
иSortedById
, оба из которых содержат «неизменяемую» ссылку наPersonCollection
(установленную в их конструкторе) и реализуютGetEnumerator
, вызывая либоcreator.GetNameSortedEnumerator
or, либоcreator.GetIdSortedEnumerator
. Такие структуры будут вести себя как ссылка на aPersonCollection
, за исключением того, что ихGetEnumerator
методы будут связаны с различными методами вPersonCollection
. Можно также иметь структуру для переноса части массива (например, можно определитьArrayRange<T>
структуру, которая будет содержатьT[]
вызываемыйArr
, intOffset
и intLength
, с индексированным свойством, которое для индексаidx
в диапазоне от 0 доLength-1
будет иметь доступArr[idx+Offset]
). К сожалению, еслиfoo
экземпляр такой структуры предназначен только для чтения, текущие версии компилятора не допускают таких операций, как,foo[3]+=4;
потому что у них нет способа определить, будут ли такие операции пытаться записывать в поляfoo
.Также возможно спроектировать структуру, которая будет вести себя как тип значения, который содержит коллекцию переменного размера (которая будет копироваться всякий раз, когда структура является структурой), но единственный способ выполнить эту работу - убедиться, что ни один объект, которому Структура содержит ссылку, которая будет когда-либо подвергаться воздействию всего, что может ее изменить. Например, можно иметь массивоподобную структуру, которая содержит закрытый массив, и чей индексированный метод «put» создает новый массив, содержимое которого аналогично содержимому оригинала, за исключением одного измененного элемента. К сожалению, может быть довольно сложно заставить такие структуры работать эффективно. Хотя бывают случаи, когда семантика структуры может быть удобной (например, возможность передавать массивоподобную коллекцию в подпрограмму, когда вызывающая сторона и вызываемая сторона знают, что внешний код не изменит коллекцию,
источник
Нет, я не совсем согласен с правилами. Это хорошие рекомендации для оценки производительности и стандартизации, но не в свете возможностей.
Как вы можете видеть из ответов, существует множество творческих способов их использования. Таким образом, эти рекомендации должны быть такими, всегда ради производительности и эффективности.
В этом случае я использую классы для представления объектов реального мира в их большей форме, я использую структуры для представления более мелких объектов, которые имеют более точное использование. То, как вы это сказали, «более сплоченное целое». Ключевое слово является связным. Классы будут более объектно-ориентированными элементами, в то время как структуры могут иметь некоторые из этих характеристик, хотя и в меньшем масштабе. ИМО.
Я часто их использую в тегах Treeview и Listview, где очень быстро можно получить доступ к общим статическим атрибутам. Я всегда изо всех сил пытался получить эту информацию другим способом. Например, в моих приложениях базы данных я использую Treeview, где у меня есть таблицы, SP, функции или любые другие объекты. Я создаю и заполняю свою структуру, помещаю ее в тег, извлекаю, получаю данные выбора и так далее. Я бы не стал делать это с классом!
Я стараюсь держать их маленькими, использую их в единичных ситуациях и не допускаю их изменения. Целесообразно осознавать память, распределение и производительность. И тестирование так необходимо.
источник
double
значений для этих координат, такая спецификация заставит его ведут себя семантически идентично структуре с открытым полем, за исключением некоторых деталей многопоточного поведения (в некоторых случаях неизменный класс был бы лучше, в то время как структура с открытым полем была бы лучше в других; так называемая «неизменяемая» структура быть хуже в каждом случае).Мое правило
1, всегда используйте класс;
2. Если есть какие-либо проблемы с производительностью, я пытаюсь изменить класс для структурирования в зависимости от правил, упомянутых @IAbstract, а затем провожу тест, чтобы увидеть, могут ли эти изменения повысить производительность.
источник
Foo
инкапсулировала фиксированный набор независимых значений (например, координат точки), которые иногда нужно передавать как группа, а иногда - независимо. Я не нашел ни одного шаблона для использования классов, который бы сочетал обе цели почти так же хорошо, как простая структура с открытыми полями (которая, будучи фиксированной коллекцией независимых переменных, идеально подходит для этой цели).public readonly
поля в моих типах, потому что создание свойств, доступных только для чтения, - это слишком много работы, практически бесполезной.)MyListOfPoint[3].Offset(2,3);
вvar temp=MyListOfPoint[3]; temp.Offset(2,3);
, преобразование , которое является поддельным , когда применяется ...Offset
методу. Правильный способ предотвращения такого поддельного кода должен состоять не в том, чтобы сделать структуры без необходимости неизменяемыми, а в том, чтобы позволить таким методам, какOffset
теги, быть помечены атрибутом, запрещающим вышеупомянутое преобразование. Неявные числовые преобразования тоже могли бы быть намного лучше, если бы их можно было пометить так, чтобы они были применимы только в тех случаях, когда их вызов был бы очевиден. Если существуют перегрузки дляfoo(float,float)
иfoo(double,double)
, я бы сказал, что попытка использовать afloat
иdouble
часто не должна применять неявное преобразование, а должна быть ошибкой.double
значенияfloat
или передача его методу, который может приниматьfloat
аргумент, но неdouble
может, почти всегда делает то, что задумал программист. В отличие от этого, присваиваниеfloat
выраженияdouble
без явного преобразования типов часто является ошибкой. Единственное время, позволяющее неявноеdouble->float
преобразование, может вызвать проблемы, - это когда будет выбрана не идеальная перегрузка. Я бы сказал, что правильный способ предотвратить это не должен был запрещать implcit double-> float, но помечать перегрузки атрибутами, чтобы запретить преобразование.Класс является ссылочным типом. Когда создается объект класса, переменная, которой назначен объект, содержит только ссылку на эту память. Когда ссылка на объект назначается новой переменной, новая переменная ссылается на исходный объект. Изменения, сделанные с помощью одной переменной, отражаются в другой переменной, поскольку оба они ссылаются на одни и те же данные. Структура является типом значения. Когда структура создается, переменная, которой назначена структура, содержит фактические данные структуры. Когда структура присваивается новой переменной, она копируется. Поэтому новая переменная и исходная переменная содержат две отдельные копии одних и тех же данных. Изменения, внесенные в одну копию, не влияют на другую копию. Как правило, классы используются для моделирования более сложного поведения или данных, которые предназначены для изменения после создания объекта класса.
Классы и структуры (Руководство по программированию в C #)
источник
МИФ № 1: СТРУКТЫ - ЛЕГКИЕ КЛАССЫ
Этот миф бывает разных форм. Некоторые люди считают, что типы значений не могут или не должны иметь методы или другое существенное поведение - их следует использовать как простые типы передачи данных, просто с открытыми полями или простыми свойствами. Тип DateTime является хорошим контрпримером к этому: имеет смысл для него быть типом значения с точки зрения фундаментальной единицы, такой как число или символ, и также имеет смысл иметь возможность выполнять вычисления на основе его ценность. Если посмотреть на вещи с другой стороны, типы передачи данных часто должны быть ссылочными типами, так как решение должно основываться на желаемом значении или семантике ссылочного типа, а не на простоте типа. Другие люди считают, что типы значений «легче», чем ссылочные типы с точки зрения производительности. Правда в том, что в некоторых случаях типы значений более производительны - им не требуется сборка мусора, если они не упакованы, не имеют накладных расходов на идентификацию типов и, например, не требуют разыменования. Но в других отношениях ссылочные типы более производительны - передача параметров, присвоение значений переменным, возвращение значений и аналогичные операции требуют только 4 или 8 байтов для копирования (в зависимости от того, используете ли вы 32-разрядный или 64-разрядный CLR ) вместо того, чтобы копировать все данные. Представьте, что ArrayList был каким-то «чистым» типом значения, и передача выражения ArrayList методу включала бы копирование всех его данных! Практически во всех случаях производительность не зависит от такого решения в любом случае. Узкие места почти никогда не бывают такими, какими вы их считаете, и прежде чем вы примете проектное решение, основанное на производительности, Вы должны измерить различные варианты. Стоит отметить, что сочетание двух убеждений тоже не работает. Не имеет значения, сколько методов имеет тип (будь то класс или структура) - память, взятая для каждого экземпляра, не затрагивается. (С точки зрения памяти, затрачиваемой на сам код, существует определенная стоимость, но это происходит один раз, а не для каждого экземпляра.)
МИФ № 2: СПРАВОЧНЫЕ ТИПЫ ЖИТЬ НА КАРТЕ; ЦЕННЫЕ ТИПЫ ЖИТЬ НА СТЕКЕ
Это часто вызвано ленью со стороны человека, повторяющего это. Первая часть верна - экземпляр ссылочного типа всегда создается в куче. Это вторая часть, которая вызывает проблемы. Как я уже отмечал, значение переменной живет везде, где она объявлена, поэтому, если у вас есть класс с переменной экземпляра типа int, значение этой переменной для любого данного объекта всегда будет там, где находятся остальные данные для объекта - в кучу. В стеке живут только локальные переменные (переменные, объявленные в методах) и параметры метода. В C # 2 и более поздних версиях даже некоторые локальные переменные в действительности не живут в стеке, как вы увидите, когда мы рассмотрим анонимные методы в главе 5. СООТВЕТСТВУЮТ ЛИ ЭТИ КОНЦЕПЦИИ СЕЙЧАС? Можно утверждать, что если вы пишете управляемый код, вы должны позволить среде выполнения беспокоиться о том, как лучше всего использовать память. Верно, спецификация языка не дает никаких гарантий относительно того, что и где живет; будущая среда выполнения может создать некоторые объекты в стеке, если знает, что это сойдет с рук, или компилятор C # может сгенерировать код, который вообще не использует стек. Следующий миф обычно является просто вопросом терминологии.
Миф № 3: объекты передаются по ссылке в C # по умолчанию
Это, наверное, самый распространенный миф. Опять же, люди, которые делают это заявление часто (хотя и не всегда), знают, как на самом деле ведет себя C #, но они не знают, что на самом деле означает «передача по ссылке». К сожалению, это сбивает с толку людей, которые знают, что это значит. Формальное определение передачи по ссылке является относительно сложным, включающим l-значения и похожую компьютерную терминологию, но важно то, что если вы передаете переменную по ссылке, вызываемый вами метод может изменить значение переменной вызывающей стороны изменив значение параметра. Теперь запомните, что значением переменной ссылочного типа является ссылка, а не сам объект. Вы можете изменить содержимое объекта, на который ссылается параметр, без передачи самого параметра по ссылке. Например,
Когда этот метод вызывается, значение параметра (ссылка на StringBuilder) передается по значению. Если бы вы изменили значение переменной построителя внутри метода - например, с помощью оператора builder = null; - это изменение не было бы замечено вызывающей стороной, вопреки мифу. Интересно отметить, что не только бит «по ссылке» мифа неточен, но и бит «объекты переданы». Сами объекты никогда не передаются ни по ссылке, ни по значению. Когда задействован ссылочный тип, либо переменная передается по ссылке, либо значение аргумента (ссылка) передается по значению. Помимо всего прочего, это отвечает на вопрос о том, что происходит, когда null используется в качестве аргумента по значению - если объекты передавались, это вызывало бы проблемы, поскольку не было бы объекта для передачи! Вместо, нулевая ссылка передается по значению так же, как и любая другая ссылка. Если это быстрое объяснение оставило вас в замешательстве, вы можете посмотреть мою статью «Передача параметров в C #» (http://mng.bz/otVt ), в котором речь пойдет гораздо подробнее. Эти мифы не единственные вокруг. Бокс и распаковка приходят из-за их значительного недопонимания, которое я постараюсь прояснить позже.
Ссылка: C # в глубине 3-е издание Джон Скит
источник
Я думаю, что хорошее первое приближение - «никогда».
Я думаю, что хорошее второе приближение - «никогда».
Если вы отчаянно нуждаетесь в совершенстве, подумайте о них, но всегда измеряйте.
источник
Я имел дело с именованным каналом Windows Communication Foundation [WCF] и заметил, что имеет смысл использовать Structs, чтобы гарантировать, что обмен данными имеет тип значения вместо ссылочного типа .
источник
Структура C # является легкой альтернативой классу. Он может делать почти то же самое, что и класс, но дешевле использовать структуру, а не класс. Причина этого несколько техническая, но в итоге новые экземпляры класса помещаются в кучу, а новые экземпляры структур помещаются в стек. Кроме того, вы не имеете дело со ссылками на структуры, как с классами, но вместо этого вы работаете непосредственно с экземпляром структуры. Это также означает, что когда вы передаете структуру функции, она является значением, а не ссылкой. Подробнее об этом рассказывается в главе о параметрах функций.
Таким образом, вы должны использовать структуры, когда вы хотите представить более простые структуры данных, особенно если вы знаете, что будете создавать их экземпляры. Существует множество примеров в .NET Framework, где Microsoft использует структуры вместо классов, например структуру Point, Rectangle и Color.
источник
Struct может быть использован для повышения производительности сборки мусора. Хотя вам обычно не нужно беспокоиться о производительности GC, есть сценарии, где это может быть убийственно. Как большие кэши в приложениях с низкой задержкой. Смотрите этот пост для примера:
http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/
источник
Типы структуры или значения могут использоваться в следующих сценариях:
Вы можете узнать больше о типах значений и типах значений здесь по этой ссылке
источник
Вкратце, используйте struct, если:
1- свойства / поля вашего объекта не нужно менять. Я имею в виду, вы просто хотите дать им начальное значение, а затем прочитать их.
2- свойства и поля в вашем объекте являются типом значения, и они не так велики.
В этом случае вы можете воспользоваться преимуществами структур для повышения производительности и оптимизации распределения памяти, поскольку они используют только стеки, а не стеки и кучи (в классах).
источник
Я редко использую структуру для вещей. Но это только я. Это зависит от того, нужен ли мне объект или нет.
Как указано в других ответах, я использую классы для объектов реального мира. У меня также есть мышление структур, которые используются для хранения небольших объемов данных.
источник
Структуры во многом похожи на классы / объекты. Структура может содержать функции, члены и может быть унаследована. Но структуры в C # используются только для хранения данных . Структуры занимают меньше ОЗУ, чем классы, и их легче собирать сборщику мусора . Но когда вы используете функции в своей структуре, компилятор фактически принимает эту структуру очень похоже на класс / объект, поэтому, если вам нужно что-то с функциями, используйте класс / объект .
источник