Когда вы используете структуру вместо класса? [закрыто]

Каковы ваши эмпирические правила о том, когда использовать structs vs. classes? Я думаю о определении C # этих терминов, но если ваш язык имеет похожие понятия, я бы тоже хотел услышать ваше мнение.

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

147 голосов | спросил RationalGeek 12 J000000Tuesday11 2011, 20:22:41

5 ответов


143

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

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

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

Например, скажем, у вас есть класс SimpleClass с двумя свойствами: A и B. Создаете экземпляр этого класса, инициализируйте A и B, а затем передайте экземпляр другому методу. Этот метод дополнительно изменяет A и B. Возврат в вызывающей функции (той, которая создала экземпляр), ваши A и B вашего экземпляра будут иметь значения, присвоенные им вызываемым методом.

Теперь вы создаете структуру. Свойства по-прежнему изменяемы. Вы выполняете те же операции с тем же синтаксисом, что и раньше, но теперь новые значения A и B не находятся в экземпляре после вызова метода. Что случилось? Итак, ваш класс теперь является структурой, что означает тип значения. Если вы передадите тип значения методу, значение по умолчанию (без ключевого слова out или ref) должно пройти «по значению»; мелкая копия экземпляра создается для использования методом, а затем уничтожается, когда метод выполняется, оставив исходный экземпляр неповрежденным.

Это становится еще более запутанным, если у вас должен быть ссылочный тип как член вашей структуры (не запрещенный, но крайне вредоносная практика практически во всех случаях); класс не будет клонирован (только ссылка на него), поэтому изменения в структуре не повлияют на исходный объект, но изменения в подклассе структуры WILL влияют на экземпляр из вызывающего кода. Это может очень легко поместить измененные структуры в очень непоследовательные состояния, которые могут вызвать ошибки далеко от того, где настоящая проблема.

По этой причине практически каждый авторитет на C # говорит, что всегда делает ваши структуры неизменными; позволяют потребителю указывать значения свойств только при построении объекта и никогда не предоставлять никаких средств для изменения значений этого экземпляра. Правилом являются только поля Readonly или свойства get-only. Если потребитель хочет изменить значение, он может создать новый объект на основе значений старого, с теми изменениями, которые они хотят, или же может вызвать метод, который будет делать то же самое. Это заставляет их рассматривать один экземпляр вашей структуры как одну концептуальную «ценность», неделимую и отличную от (но, возможно, равной) всех остальных. Если они выполняют операцию с «значением», хранящимся в вашем типе, они получают новое «значение», которое отличается от их начального значения, но все же сопоставимо и /или семантически равнозначно.

В качестве хорошего примера рассмотрим тип DateTime. Вы не можете назначить какое-либо из полей экземпляра DateTime напрямую; вы должны либо создать новый, либо вызвать метод существующего, который будет создавать новый экземпляр. Это связано с тем, что дата и время являются «значением», например, числом 5, а изменение числа 5 приводит к новому значению, которое не равно 5. Просто потому, что 5 + 1 = 6 не означает, что 5 теперь 6 потому что вы добавили 1 к нему. DateTimes работают одинаково; 12:00 не «становится» 12:01, если вы добавите минута, вместо этого вы получите новое значение 12:01, которое отличается от 12:00. Если это логичное состояние дел для вашего типа (хорошие концептуальные примеры, которые не встроены в .NET, это деньги, расстояние, вес и другие количества UOM, где операции должны учитывать все части стоимости) затем используйте структуру и спроектируйте ее соответствующим образом. В большинстве других случаев, когда подпункты объекта должны быть независимо изменены, используйте класс.

ответил KeithS 12 J000000Tuesday11 2011, 21:16:00
11

Ответ: «Использовать структуру для чистых конструктов данных и класс для объектов с операциями» определенно неверно. ИМО. Если структура содержит большое количество свойств, то класс почти всегда более уместен. Microsoft часто говорит, с точки зрения эффективности, если ваш тип больше 16 байт, он должен быть классом.

http://msdn.microsoft.com/en-us /library/ah19swz4%28v=vs.71%29.aspx

ответил bytedev 11 TueEurope/Moscow2012-12-11T14:20:47+04:00Europe/Moscow12bEurope/MoscowTue, 11 Dec 2012 14:20:47 +0400 2012, 14:20:47
9

Самое важное отличие между class и struct - это то, что происходит в следующей ситуации:

  Вещь вещь1 = новая Вещь ();
  вещь1.somePropertyOrField = 5;
  Вещь вещь2 = вещь1;
  thing2.somePropertyOrField = 9;

Что должно быть последствием последнего оператора в thing1.somePropertyOrField? Если Thing a struct, а somePropertyOrField - открытое общедоступное поле, объекты thing1 и thing2 будут «отсоединены» «друг от друга, поэтому последний оператор не повлияет на thing1. Если Thing - это класс, то thing1 и thing2 будут прикреплены друг к другу, и поэтому последний оператор напишет thing1.somePropertyOrField. Следует использовать структуру в тех случаях, когда бывшая семантика имела бы больше смысла и должна использовать класс в тех случаях, когда последняя семантика будет иметь больше смысла.

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

Например, рассмотрим утверждения:

  Person somePerson = myPeople.GetPerson ("123-45-6789");
  somePerson.Name = "Mary Johnson"; //Была «Мэри Смит»

Будет ли второй оператор изменять информацию, хранящуюся в myPeople? Если Person - структура с открытым полем, это не будет, , и тот факт, что он не будет явным следствием того, что он является структурой открытого поля ; if Person - это структура и хочет обновить myPeople, нужно было бы сделать что-то вроде myPeople.UpdatePerson("123-45-6789", somePerson). Однако, если Person является классом, может быть намного сложнее определить, не будет ли приведенный выше код никогда не обновлять содержимое MyPeople, всегда обновлять его или иногда обновлять.

В отношении понятия, что структуры должны быть «неизменными», я вообще не согласен. Имеются допустимые случаи использования для «неизменяемых» структур (где инварианты применяются в конструкторе), но требующие, чтобы вся структура была переписана в любое время, когда какая-либо ее часть будет неудобной, расточительной и более склонной к ошибкам, чем просто разоблачение полей. Например, рассмотрим структуру PhoneNumber, поля которой, среди прочих, включают AreaCode и Exchange, и предположим, что у одного есть List<PhoneNumber>. Эффект от следующего должен быть довольно ясным:

  for (int i = 0; i <myList.Count; i ++)
  {
    PhoneNumber theNumber = myList [i];
    if (theNumber.AreaCode == "312")
    {
      string newExchange = "";
      if (new312to708Exchanges.TryGetValue (theNumber.Exchange), вне newExchange)
      {
        theNumber.AreaCode = "708";
        theNumber.Exchange = newExchange;
        myList [i] = theNumber;
      }
    }
  }

Обратите внимание, что ничто в приведенном выше коде не знает или не заботится о любых полях в PhoneNumber, кроме AreaCode и Exchange. Если PhoneNumber был так называемой «неизменяемой» структурой, было бы необходимо либо обеспечить метод withXX для каждого поля, что бы вернуть новый экземпляр структуры, который был сохранен значение переданного значения в указанном поле, или же необходимо, чтобы код, подобный выше, знал о каждом поле в структуре. Не совсем привлекательный.

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

  1. Семантика структуры указывает, что она сохраняет идентификатор рассматриваемого объекта, а не как ярлык для хранения свойств объекта. Например, «KeyValuePair» будет хранить идентификаторы определенных кнопок, но не ожидается, что они будут содержать постоянную информацию об этих положениях кнопок, состояния подсветки и т. Д.
  2. Структура знает, что она содержит единственную ссылку на этот объект, никто другой не получит ссылку, и любые мутации, которые когда-либо будут выполняться для этого объекта, будут выполнены до того, как ссылка на нее будет храниться где угодно.

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

ответил supercat 7 J000000Saturday12 2012, 01:59:00
6

Я склонен использовать структуры только тогда, когда нужен один метод, поэтому класс кажется слишком «тяжелым». Поэтому иногда я рассматриваю структуры как легкие объекты. ОСТОРОЖНО: это не всегда работает, и это не всегда лучшая вещь, поэтому это действительно зависит от ситуации!

ДОПОЛНИТЕЛЬНЫЕ РЕСУРСЫ

Для более формального объяснения MSDN говорит: http://msdn.microsoft .com /EN-US /библиотека /ms229017.aspx Эта ссылка проверяет, что я упоминал выше, структуры должны использоваться только для хранения небольшого объема данных.

ответил rrazd 12 J000000Tuesday11 2011, 20:25:57
2

Используйте конструкцию для чистых конструктов данных и класс для объектов с операциями.

Если ваша структура данных не нуждается в управлении доступом и не имеет специальных операций, кроме get /set, используйте struct. Это делает очевидным, что вся эта структура является контейнером для данных.

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

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

ответил Michael K 12 J000000Tuesday11 2011, 20:49:46

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132