C #: Какой стиль контейнеров данных предпочтительнее в целом? - PullRequest
13 голосов
/ 13 июля 2009

При создании простого класса контейнера данных, каким он должен быть?

  • Класс или структура?
  • Изменчивый или неизменный?
  • С или без непустого конструктора?

Примеры вышеперечисленного:

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;
    }
}

Есть веские причины, по которым мы должны выбирать одно над другим? Или существуют в основном субъективные предпочтения, которые их разделяют? Или это сильно зависит от конкретных случаев использования? Если да, то в каких случаях следует выбирать, что и почему?

Ответы [ 8 ]

11 голосов
/ 13 июля 2009

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

Конструктор без параметров удобен для изменяемых данных, если вы собираетесь связывать данные, поскольку это позволяет системе создавать экземпляры без дополнительного кода от вас. Непустой конструктор очень важен для неизменяемых данных. Для изменяемых данных инициализатор объекта проходит долгий путь к этому (хотя это не совсем то же самое с точки зрения проверки и т. Д.):

var obj = new Person {Name="Fred", DateOfBirth=DateTime.Today};

Является ли ваш тип неизменным, зависит от вас; mutable облегчает привязку данных и сериализацию. В общем, вы склонны видеть более изменчивые типы в .NET, но это может измениться, когда мы войдем в параллельную / многоядерную эру.

3 голосов
/ 13 июля 2009
  • Вы почти всегда должны предпочитать классы структурам. Используйте структуры только тогда, когда объект представляет (неизменяемое) значение.

  • Если вам нужно изменить объект, и это безопасно сделать, сделайте его изменяемым, иначе сделайте его неизменным и используйте клонирование.

  • Если объект находится в допустимом состоянии при создании с помощью конструктора по умолчанию, хорошо. В противном случае всегда предоставьте свой собственный конструктор.

2 голосов
/ 13 июля 2009

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

Кроме этого, я думаю, что это сводится к личным или командным предпочтениям и вашим конкретным требованиям.

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

1 голос
/ 13 июля 2009

Пара баллов по классам против структур:

Классы передаются по ссылке, что означает, что один и тот же объект экземпляра передается между функциями. Если вы создаете MyClass в методе A и вызываете метод B, метод B меняет класс и ничего не возвращает методу A, метод A видит изменения, сделанные в MyClass, потому что они ссылаются на один и тот же объект. Ссылка обычно представляет собой int32, поэтому независимо от того, насколько велик ваш класс, он будет быстро вызывать метод B, потому что он пропускает только 4 байта. Классы полагаются на сборщик мусора, чтобы решить, когда он больше не используется (меньшие накладные расходы при передаче класса вокруг, но увеличивают накладные расходы сборщику мусора).

Структуры передаются по значению (или типу значения). Это означает, что вся структура копируется при передаче. Если ваша структура большая, это займет много времени. Изменение в структуре в методе B не будет отображаться в методе A, если только оно не будет возвращено (опять же, будет стоить времени, поскольку оно передается по значению), а метод A считывает возвращаемое значение. Структуры создаются в стеке и не требуют дополнительных затрат на сборку мусора (это можно увидеть, изучив код IL).

Существует множество ограничений в структурах по сравнению с классами, таких как отсутствие виртуальных методов и других полиморфных функций.

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

0 голосов
/ 13 июля 2009

Похоже, что почти все временные структуры являются ненужным отвлечением от легкости и эффективности занятий.

0 голосов
/ 13 июля 2009

Подумайте об использовании классов, когда вам нужно предоставить объекту "функциональность", а не просто идентификацию или состояние, то есть "значения".

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

0 голосов
/ 13 июля 2009

Также существует эмпирическое правило, согласно которому структуры должны быть не длиннее 16 байт - размер строки кэша процессора.

0 голосов
/ 13 июля 2009

Я обычно делаю неизменные классы со всеми значениями, которые устанавливаются / передаются через конструктор.

...