Что лучше для хранилища данных Struct/Classes?
Мы видели много дискуссий в SO относительно класса vs struct в C#. Главным образом заканчиваются выводы о том, что это выделение памяти в куче / стеке. И рекомендую использовать структуры в небольших структурах данных.
Теперь у меня есть ситуация, чтобы выбрать простое хранилище данных среди этих двух вариантов. В нашем приложении есть тысячи классов, которые действуют как простые хранилища данных (только открытые открытые поля), и они передаются между различными модулями и сервисами.
Насколько я понимаю, я чувствовал, что лучше пойти дальше с struct вместо классов по соображениям производительности. Потому что это простые структуры данных, которые действуют только как хранилища данных.
Прежде чем продолжить, мне нужен совет специалиста от людей, которые пережили эту борьбу.
- мое понимание верно?
- Я видел, что большинство ORM имеют классы в качестве хранилищ данных. Поэтому я сомневаюсь, что должна быть причина, чтобы продолжать занятия вместо структур. что бы это было?
9 ответов
Я бы сделал выбор исходя из следующих критериев
- ссылочный тип против семантики типа значения. Если 2 объекта равны, только если это один и тот же объект, это указывает на семантику ссылочного типа => класс. Если значение его членов определяет равенство (например, 2 DateTimes равны, если оба представляют один и тот же момент времени, даже если они представляют собой 2 разных объекта), тип значения semantics => struct
- След памяти объекта. Если объект огромен и часто размещается, то его создание структурой потребляет стек намного быстрее, поэтому я бы предпочел, чтобы он был классом. Напротив, я бы предпочел избежать штрафа GC для типов малых значений; следовательно, сделайте их структурой.
- Вы можете сделать объект неизменным? Я считаю, что структуры отлично подходят для "объектов стоимости" - из книги DDD.
- Вы столкнетесь с какими-либо штрафами за распаковку бокса в зависимости от использования этого объекта? Если да, иди на урок.
Довольно классное, не очень известное преимущество Structs по сравнению с классами состоит в том, что в структурах есть автоматическая реализация GetHashcode и Equals.
Это очень полезно, когда для словарей требуются ключи
Реализация структуры GetHashcode и Equals основана на двоичном содержимом экземпляров структуры + отражение для ссылочных членов (например, членов String и других экземпляров классов).
Таким образом, следующий код работает для GethashCode/Equals:
public struct Person
{
public DateTime Birthday { get; set; }
public int Age{ get; set; }
public String Firstname { get; set; }
}
class Program
{
static void Main(string[] args)
{
Person p1 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" };
Person p2 = new Person { Age = 44, Birthday = new DateTime(1971, 5, 24), Firstname = "Emmanuel" };
Debug.Assert(p1.Equals(p2));
Debug.Assert(p1.GetHashCode() == p2.GetHashCode());
}
}
Оба утверждения завершаются успешно, когда Person является структурой. Оба утверждения не выполняются, если Person является классом, а не структурой.
Ссылка: https://msdn.microsoft.com/en-Us/library/2dts52z7%28v=vs.110%29.aspx
С уважением, лучшее кодирование
Структуры должны быть определены неизменяемыми, где в классах не должно быть. Если вы думаете, что ваши объекты будут маленькими и неизменными, вы можете сделать их структурами или позволить им быть классами.
Я никогда не могу вспомнить, какие именно структуры различаются, но они есть. Тонкими способами. На самом деле, иногда они приходят и кусают тебя.
Так. Если вы не знаете, что делаете, просто придерживайтесь уроков.
Я знаю, это звучит немного новичок. Я знаю, что я должен прямо сейчас пойти посмотреть на различия и показать их здесь - но это уже было сделано другими. Все, что я хочу сказать, это то, что добавление объектов другого типа создает семантическую нагрузку, немного дополнительной сложности, которую вы должны тщательно рассмотреть.
Если я правильно помню, одной из самых больших проблем является семантика значений структур: их передача приведет к появлению различных объектов (поскольку они передаются по значению). Если вы затем измените какое-либо поле в одном месте, имейте в виду, что во всех других местах поле не изменилось! Вот почему каждый рекомендует неизменность для структур!
РЕДАКТИРОВАТЬ: Для случая, который вы описываете, структуры не будут работать!
Преимущество объекта класса в том, что можно передавать ссылку на него, причем область действия и время жизни такой ссылки не ограничены, если он достигает внешнего кода. Преимущество структуры состоит в том, что, хотя можно передавать недолговечные ссылки на них, невозможно обойтись постоянными беспорядочными ссылками. Это помогает избежать беспокойства о том, существуют ли такие ссылки.
Некоторые люди предполагают, что носители данных, которые могут изменяться, не должны быть структурами. Я категорически не согласен. Сущности, которые существуют для хранения данных, во многих случаях должны быть структурами, особенно если они изменчивы. Эрик Липперт много раз писал, что считает изменяемые типы значений злыми (ищите по тегам "изменяемый" и "структура"). Это, безусловно, правда, что.net позволяет выполнять определенные операции с изменяемыми структурами, чего не следует делать, и не позволяет некоторые вещи делать так, как следует, но структуры POD ("Plain Old Data"), которые не имеют методов мутации, но вместо этого выставляйте все свое состояние через открытые поля, имейте очень полезную согласованность в своем поведении, которое не разделяется ни с каким другим типом данных. Использование структуры POD может сбить с толку тех, кто не знаком с тем, как они работают, но сделает программу намного более читаемой для всех, кто это делает.
Рассмотрим, например, следующий код, предполагая, что EmployeeInfoStruct не содержит ничего, кроме типов значений и типов неизменяемых классов, таких как String:
[employeeInfoStruct - это структура, содержащая следующее поле] публичный десятичный годовой бонус; [someEmployeeContainer является экземпляром класса, который включает следующий метод] EmployeeInfoStruct GetEmployeeInfo(String id); // Просто подпись - код не имеет значения [какой-то другой метод использует следующий код] EmployeeInfoStruct anEmployee = someEmployeeContainer.GetEmployeeInfo("123-45-6789"); anEmployee.YearlyBonus += 100;
Эрик Липперт жалуется, что приведенный выше код изменит значение в anEmployee, но это изменение не повлияет на контейнер. Я хотел бы предположить, что это хорошо - любой, кто знает, как работает структура, может взглянуть на приведенный выше код и знает, что запись в переменную структуры повлияет на эту переменную, но не повлияет на что-либо еще, если в дальнейшем программа не использует какой-либо другой метод (возможно, SetEmployeeInfo) для хранения этой переменной где-нибудь.
Теперь замените EmployeeInfoStruct на EmployeeInfoClass, который имеет свойство чтения / записи типа YearlyBonus. Используя только приведенную выше информацию, что можно сказать о взаимосвязи между записями в someEmployeeContainer и anEmployee? В зависимости от реализаций класса anEmployee (который, если EmployeeInfoClass не закрыт, может быть или не может быть EmployeeInfoClass) и someEmployeeContainer, отношения между объектами могут быть любыми. Пишет одному может:
- Не влияет на других
- Обновите другой "естественным" способом
- Повредить другого каким-то произвольным образом
С структурами, содержащими только поля типов значений или неизменяемых классов, семантика всегда будет #1. Чтобы узнать это, не нужно смотреть ни на код самой структуры, ни на код контейнера. Напротив, если anEmployee.Salary или someEmployeeContainer.GetEmployee являются виртуальными, невозможно реально знать, какой будет семантика.
Важно отметить, что, если структуры большие, передача их по значению или возврат из функций может быть дорогостоящим. Обычно лучше передавать большие структуры как ref
параметры, когда это возможно. Несмотря на то, что встроенные коллекции действительно не способствуют такому использованию, это может сделать использование структуры размером в сотни байтов дешевле, чем использование класса.
Комментарий о неизменности структур является правильным. И это то, что может вас укусить. Вы можете определить структуры с помощью установщиков полей, но при изменении значения поля создается новый экземпляр. Таким образом, если вы удерживаете ссылку на старый объект, он все равно будет ссылаться на старое значение. По этой причине я не люблю использовать изменяемые структуры, так как это может привести к появлению тонких и сложных ошибок (особенно если вы используете сложные составные операторы).
С другой стороны, есть много веских причин для использования классов с неизменяемым состоянием (думаю, строка).
Я помню один совет, данный на MSDN, что структура не должна превышать 16 или 21 байт. Ищу ссылку, но пока не могу ее найти.
Основным выводом было то, что если у вас есть строка в вашем типе данных - сделайте ее классом, не задумываясь. В противном случае структура не должна содержать много.
Я думаю, у вас есть правильная идея. Структуры сделаны, чтобы подражать типам данных. Они основаны на ценностях, а не на основе ссылок. Если вы посмотрите документацию MSDN для большинства базовых классов данных (int, double, decimal и т. Д.), То все они основаны на структурах. При этом, однако, структуры не должны быть чрезмерно использованы по той же причине. Пространство для хранения всего, что находится в этой структуре, выделяется, как только оно создается, а классы просто выделяют пространство для ссылки на все внутри. Если данные находятся в достаточно маленьких порциях, где это не является проблемой, тогда лучше всего использовать структуры. Если это проблема, идите с классами. Если вы не знаете, то лучше всего придерживаться того, с чем вы знакомы.
Если у вас низкие требования к задержке и много объектов, медленная сборка мусора может стать проблемой. В этом случае структура может быть очень полезной, поскольку сборщику мусора не нужно сканировать иерархию типов значений, если типы значений не содержат никаких ссылочных типов.
Вы можете найти тест здесь: http://00sharp.wordpress.com/2013/07/03/a-case-for-the-struct/