С#: Какой стиль контейнеров данных предпочтительнее вообще?
При создании простого класса контейнера данных, что это должно быть?
- Класс или структура?
- Mutable или неизменяемый?
- С или без непустого конструктора?
Примеры выше:
struct MutableStruct
{
public string Text { get; set; }
public int Number { get; set; }
}
struct ImmutableStruct
{
public string Text { get; private set; }
public int Number { get; private set; }
public ImmutableStruct(string text, int number)
: this()
{
Text = text;
Number = number;
}
}
struct MutableStructWithConstructor
{
public string Text { get; set; }
public int Number { get; set; }
public MutableStructWithConstructor(string text, int number)
: this()
{
Text = text;
Number = number;
}
}
class MutableClass
{
public string Text { get; set; }
public int Number { get; set; }
}
class ImmutableClass
{
public string Text { get; private set; }
public int Number { get; private set; }
public ImmutableClass(string text, int number)
{
Text = text;
Number = number;
}
}
class MutableClassWithConstructor
{
public string Text { get; set; }
public int Number { get; set; }
public MutableClassWithConstructor(string text, int number)
{
Text = text;
Number = number;
}
}
Любые веские причины мы должны выбрать один над другим? Или существуют ли в основном субъективные предпочтения, которые их разделяют? Или это сильно зависит от конкретных случаев использования? Если да, то в каких случаях следует выбирать, что и почему?
Ответы
Ответ 1
Почти всегда класс; structs действительно должны использоваться только для вещей, которые являются значениями - например, пары с комплексным числом или пары валютных типов/значений - и должны быть почти без исключения неизменными.
Конструктор без параметров удобен в изменяемых данных, если вы собираетесь выполнять привязку данных, так как это позволяет системе создавать экземпляры без дополнительного кода от вас самих. Непустой конструктор очень важен для неизменяемых данных. Для изменяемых данных инициализатор объектов имеет большой путь к этому (хотя это не совсем то же самое с точки зрения проверки и т.д.):
var obj = new Person {Name="Fred", DateOfBirth=DateTime.Today};
Являются ли ваши типы неизменными, зависит от вас; mutable упрощает обработку данных и сериализацию. В общем, вы, как правило, видите больше изменяемых типов в .NET, но это может измениться, когда мы переходим к параллельной/многоядерной эпохе.
Ответ 2
-
Вы почти всегда предпочитаете классы над структурами. Используйте структуры только тогда, когда объект представляет (неизменяемое) значение.
-
Если вам нужно изменить объект, и это безопасно сделать, тогда сделайте его изменчивым, иначе сделайте его неизменным и используйте клонирование.
-
Если объект находится в допустимом состоянии при создании с помощью конструктора по умолчанию, отлично. В противном случае всегда предоставляйте свой собственный конструктор.
Ответ 3
Используйте только структуры, когда вам нужна семантика значения-типа, и старайтесь полностью исключить измененные структуры.
Кроме этого, я думаю, что это сводится к личным или командам, предпочтениям и вашим конкретным требованиям.
(В наши дни я пытаюсь использовать неизменяемые объекты, когда это возможно, что почти всегда требует передачи значений в конструктор.)
Ответ 4
Несколько точек в классах vs structs:
Классы являются pass-by-reference, что означает, что один и тот же экземпляр объекта передается между функциями. Если вы создаете MyClass в методе A и вызывают метод B, метод B изменяет класс и ничего не возвращает методу A, метод A видит изменения, внесенные в MyClass, потому что они ссылаются на один и тот же объект. Ссылка обычно представляет собой int32, поэтому независимо от того, насколько большой ваш класс, это будет быстро вызвать метод B, поскольку он пропускает только 4 байта. Классы полагаются на сборщик мусора, чтобы решить, когда он больше не используется (меньшие накладные расходы при прохождении класса вокруг, но добавляет накладные расходы сборщику мусора).
Структуры являются передаваемыми по значению (или типом значений). Это означает, что вся структура копируется, когда она передается. Если ваша структура большая, это займет много времени. Изменение структуры в методе B не будет отображаться в методе A до тех пор, пока оно не будет возвращено (снова затраченное время, так как оно проходит по значению), а метод A считывает возвращаемое значение. Структуры создаются в стеке и не имеют накладных расходов на сбор мусора (это можно увидеть, если вы изучите свой IL-код).
В структурах существует множество ограничений по сравнению с классами, таких как отсутствие виртуальных методов и другие полиморфные функции.
Лучше всего использовать классы, если только вы не собираетесь быстро создавать и отбрасывать множество объектов в одном и том же методе (которые истощают системные ресурсы из-за сбора мусора), и в этом случае предпочитают структуры.
Ответ 5
Я обычно выполняю неизменяемые классы со всеми значениями, которые устанавливаются/передаются через конструктор.
Ответ 6
Кроме того, существует правило, что структуры не должны быть больше 16 байт - размер строки кэша процессора.
Ответ 7
Рассмотрите возможность использования классов всякий раз, когда вам нужно предоставить объектную "функциональность", а не просто идентификацию или состояние, то есть "значения".
Как уже было сказано, структуры являются типами значений и как таковые будут скопированы везде, где вы их используете. Кроме того, при создании экземпляров классов и структур существует незначительная разница в производительности.
Ответ 8
Кажется, что почти все структуры времени являются ненужным отвлечением от простоты и эффективности классов.