2009-07-13 3 views
13

При создании простого класса контейнера данных, каким он должен быть?C#: Какой тип контейнеров данных предпочтительнее в целом?

  • Класс или структура?
  • Мощный или непреложный?
  • С или без непустого конструктора?

Примеры выше:

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

Кстати, ваши «ImmutableStruct» и «ImmutableClass» не являются поистине неизменными: свойства имеют частные сеттеры, но их состояние может быть изменено из самого struct/класса. (Это ярлык, который я часто использую сам, но было бы лучше избегать автоматического свойства и вместо этого использовать переменную-член только для чтения, открытую через публичный getter.) – LukeH

+0

Хм, это на самом деле хороший момент, хотя я думаю Мне может быть слишком ленив, чтобы что-то сделать: p Особенно потому, что простой контейнер данных не должен ничего делать с его данными. То, как это изнутри, имеет для меня меньшее значение ... Хотя, возможно, публичное поле для чтения было бы лучше? Вид вроде свойств, хотя ...: p – Svish

+0

Hmm ... 'public string Text {get; } 'должен создать поле для чтения только для чтения и включить настройку только в конструкторе, например, с полями readonly. На данный момент я думаю, что строка создает бесполезное свойство, возвращающее null ... – Svish

ответ

10

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

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

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

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

+0

Спасибо за очень хорошее объяснение того, какие структуры следует использовать! Никогда раньше это не получалось, но теперь это стало намного больше смысла :) – Svish

+0

По вопросу «mutable облегчает работу с привязкой данных». Я бы сказал, что mutable будет даже требоваться, по крайней мере, для двусторонней передачи данных, не так ли? Immutable ограничит вас односторонней привязкой данных. Я думаю? И если это правильно, я бы сказал, что это вопрос того, что вам нужно, а не облегчать ситуацию ... – Svish

+0

@Svish - это зависит от конкретного сценария; существуют способы выполнения привязки данных к неизменяемым данным (например, как PropertyGrid int VS IDE * иногда позволяет вам редактировать глубоко неизменяемые данные), но для этого требуется использование тайных интерфейсов. –

0

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

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

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

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

2

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

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

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

0

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

+0

Хм, разве это не зависит от процессора? Или это правда для всех из них, прошлого и настоящего? – Svish

+0

Это было бы. Кэш-линии всех современных x86-процессоров, используемых в настольных компьютерах, имеют ширину не менее 16 байт и, вероятно, не шире 32. – CannibalSmith

1

Пару точек на классы против структур:

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

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

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

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

+0

Это не вопрос о том, какая разница между классом и структурой, хотя ... Это было то, вы используете, почему, и должны ли они быть изменяемыми или неизменными, когда речь заходит о простых структурах данных без метода =) – Svish

+0

Конечно. Неизменяемый защитный способ программирования, есть меньше вещей, чтобы «пойти не так», например. вы не ожидали, что кто-то установит Text в null и по-прежнему будет заполнять Number, выполнив конкретный вызов метода. Но ... В вашем случае нет методов, и их никогда не будет. Поэтому нет смысла использовать неизменяемые по этой причине. Следующая причина использования неизменяемости заключается в том, что окружающие функции не ожидали, что значение, содержащееся в объекте, изменится (определенным образом) - неизменное все еще имеет смысл для вас здесь. –

+0

Вы не будете делать много использовать полиморфизм без методов так ... Порядок предпочтения в вас случай: 1: Неизменный класс 2: Неизменный структура 3: Изменчивый класс 4: Mutable структура Если вы не страдаете от проблемы производительности сборки мусора, обсуждавшиеся в моем посте, и в этом случае: 1: Неизменный структура 2: Mutable структуры 3: Неизменный класс 4: Изменяемый класс –

0

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

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

0

Кажется, что почти все время структуры являются ненужным отвлечением от простоты и эффективности классов.

Смежные вопросы