Каковы ваши практические правила, когда использовать структуры против классов? Я думаю об определении этих терминов в C #, но если ваш язык имеет схожие понятия, я бы тоже хотел услышать ваше мнение.
Я склонен использовать классы практически для всего и использовать структуры только тогда, когда что-то очень упрощено и должно иметь тип значения, например PhoneNumber или что-то в этом роде. Но это кажется относительно незначительным использованием, и я надеюсь, что есть более интересные варианты использования.
Ответы:
Общее правило, которое следует соблюдать, состоит в том, что структуры должны быть небольшими, простыми (одноуровневыми) коллекциями связанных свойств, которые являются неизменяемыми после создания; для всего остального используйте класс.
C # хорош тем, что структуры и классы не имеют явных различий в объявлении, кроме ключевого слова определения; поэтому, если вы чувствуете, что вам необходимо «обновить» структуру до класса или, наоборот, «понизить» класс до структуры, это в основном простой вопрос изменения ключевого слова (есть несколько других ошибок; структуры не могут быть получены из любого другого класса или типа структуры, и они не могут явно определить конструктор по умолчанию без параметров).
Я говорю «в основном», потому что более важная вещь, которую нужно знать о структурах, заключается в том, что, поскольку они являются типами значений, их обращение с классами (ссылочными типами) может закончиться болью и половиной. В частности, изменение свойств структуры может вызвать неожиданное поведение.
Например, скажем, у вас есть класс SimpleClass с двумя свойствами, A и B. Вы создаете копию этого класса, инициализируете A и B, а затем передаете экземпляр другому методу. Этот метод дополнительно изменяет A и B. Возвращаясь к вызывающей функции (той, которая создала экземпляр), A и B вашего экземпляра будут иметь значения, данные им вызываемым методом.
Теперь вы делаете это структурой. Свойства все еще изменчивы. Вы выполняете те же операции с тем же синтаксисом, что и раньше, но теперь новые значения A и B отсутствуют в экземпляре после вызова метода. Что случилось? Ну, теперь ваш класс - это структура, то есть тип значения. Если вы передаете тип значения методу, по умолчанию (без ключевого слова out или ref) передается «по значению»; поверхностная копия экземпляра создается для использования методом, а затем уничтожается, когда метод завершается, оставляя исходный экземпляр без изменений.
Это становится еще более запутанным, если вы должны иметь ссылочный тип в качестве члена вашей структуры (не запрещено, но крайне плохая практика практически во всех случаях); класс не будет клонирован (только ссылка на структуру), поэтому изменения в структуре не будут влиять на исходный объект, но изменения в подклассе структуры будут влиять на экземпляр из вызывающего кода. Это может очень легко привести изменчивые структуры в очень противоречивые состояния, которые могут привести к ошибкам далеко от того места, где находится настоящая проблема.
По этой причине практически каждый авторитет в C # говорит, что всегда нужно делать ваши структуры неизменяемыми; позволяют потребителю указывать значения свойств только при построении объекта и никогда не предоставляют никаких средств для изменения значений этого экземпляра. Поля только для чтения, или свойства только для получения, являются правилом. Если потребитель хочет изменить значение, он может создать новый объект на основе значений старого, с желаемыми изменениями, или он может вызвать метод, который будет делать то же самое. Это заставляет их рассматривать один экземпляр вашей структуры как одну концептуальную «ценность», неделимую и отличную от других (но, возможно, приравненную к ней). Если они выполняют операцию над «значением», сохраненным вашим типом, они получают новое «значение», которое отличается от их начального значения,
Для хорошего примера посмотрите на тип DateTime. Вы не можете назначить любое из полей экземпляра DateTime напрямую; Вы должны либо создать новый, либо вызвать метод для существующего, который создаст новый экземпляр. Это потому, что дата и время являются «значением», таким как число 5, а изменение числа 5 приводит к новому значению, которое не равно 5. Просто потому, что 5 + 1 = 6 не означает, что 5 теперь 6 потому что вы добавили 1 к нему. DateTimes работают так же; 12:00 не «становится» 12:01, если вы добавите минуту, вместо этого вы получите новое значение 12:01, отличное от 12:00. Если для вашего типа это логичное положение вещей (хорошими концептуальными примерами, которые не встроены в .NET, являются «Деньги», «Расстояние», «Вес» и другие объемы UOM, где операции должны учитывать все части значения), затем используйте структуру и спроектируйте ее соответственно. В большинстве других случаев, когда подпункты объекта должны быть независимо изменяемыми, используйте класс.
источник
Ответ: «Использовать структуру для чистых конструкций данных и класс для объектов с операциями» - это определенно неверный IMO. Если структура содержит большое количество свойств, то класс почти всегда более уместен. Microsoft часто говорит, с точки зрения эффективности, если ваш тип больше 16 байт, это должен быть класс.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
источник
Самое важное различие между a
class
и astruct
состоит в том, что происходит в следующей ситуации:На что должно повлиять последнее утверждение
thing1.somePropertyOrField
? ЕслиThing
структура иsomePropertyOrField
является открытым публичным полем, то объектыthing1
иthing2
будут «отсоединены» друг от друга, поэтому последнее утверждение не повлияетthing1
. ЕслиThing
это класс, тоthing1
иthing2
будут прикреплены друг к другу, и поэтому последнее утверждение напишетthing1.somePropertyOrField
. Следует использовать структуру в случаях, когда первая семантика имела бы больше смысла, и следует использовать класс в случаях, когда последняя семантика имела бы больше смысла.Обратите внимание, что, хотя некоторые люди советуют, что желание сделать что-то изменчивое является аргументом в пользу того, что это класс, я бы сказал, что верно обратное: если что-то, что существует для хранения некоторых данных, будет изменчивым, и если не будет очевидно, прикреплены ли экземпляры к чему-либо, вещь должна быть структурой (вероятно, с открытыми полями), чтобы прояснить, что экземпляры не привязаны ни к чему другому.
Например, рассмотрим утверждения:
Изменит ли второе утверждение информацию, хранящуюся в
myPeople
? ЕслиPerson
это структура с открытым полем, это не так, и тот факт, что она не будет, будет очевидным следствием того, что она является структурой с открытым полем ; ЕслиPerson
это структура и кто-то хочет обновитьmyPeople
, то явно нужно сделать что-то вродеmyPeople.UpdatePerson("123-45-6789", somePerson)
. Однако, еслиPerson
это класс, может быть гораздо сложнее определить, будет ли приведенный выше код никогда не обновлять содержимоеMyPeople
, всегда обновлять его или иногда обновлять.Что касается понятия, что структуры должны быть «неизменными», я не согласен в целом. Существуют допустимые варианты использования для «неизменяемых» структур (где инварианты применяются в конструкторе), но требуется, чтобы вся структура была переписана в любое время, когда любая ее часть изменяется, неудобно, расточительно и более склонно вызывать ошибки, чем просто разоблачать поля напрямую. Например, рассмотрим
PhoneNumber
структуру, поля которой включают, среди прочего,AreaCode
иExchange
, и предположим, что у каждого естьList<PhoneNumber>
. Эффект следующего должен быть довольно очевидным:Обратите внимание, что ничто в приведенном выше коде не знает и не заботится о каких-либо полях,
PhoneNumber
кромеAreaCode
иExchange
. Если быPhoneNumber
это была так называемая «неизменяемая» структура, было бы необходимо либо предоставитьwithXX
метод для каждого поля, который бы возвращал новый экземпляр структуры, который содержал переданное значение в указанном поле, либо это было бы необходимо чтобы код, подобный приведенному выше, знал о каждом поле в структуре. Не совсем привлекательный.Кстати, есть как минимум два случая, когда структуры могут разумно содержать ссылки на изменяемые типы.
В первом сценарии IDENTITY объекта будет неизменным; во-вторых, класс вложенного объекта может не обеспечивать неизменность, но структура, содержащая ссылку, будет.
источник
Я склонен использовать структуры только тогда, когда нужен один метод, поэтому класс кажется слишком «тяжелым». Таким образом, иногда я отношу структуры к легким объектам. ОСТЕРЕГАЙТЕСЬ: это не всегда работает и не всегда является лучшим решением, так что это действительно зависит от ситуации!
ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ
Для более формального объяснения MSDN говорит: http://msdn.microsoft.com/en-us/library/ms229017.aspx Эта ссылка подтверждает то, что я упоминал выше, структуры должны использоваться только для размещения небольшого количества данных.
источник
Используйте структуру для чистых конструкций данных и класс для объектов с операциями.
Если ваша структура данных не нуждается в управлении доступом и не имеет специальных операций, кроме get / set, используйте struct. Это делает очевидным, что вся эта структура является контейнером для данных.
Если в вашей структуре есть операции, которые изменяют ее более сложным способом, используйте класс. Класс также может взаимодействовать с другими классами через аргументы методов.
Думайте об этом как о разнице между кирпичом и самолетом. Кирпич - это структура; имеет длину, ширину и высоту. Это не может сделать многое. Самолет может иметь несколько операций, которые каким-то образом изменяют его, например, перемещение поверхностей управления и изменение тяги двигателя. Это было бы лучше как класс.
источник