Неизменность структур
Возможный дубликат:
Почему изменчивые структуры злые?
Я читал это во многих местах, в том числе и здесь, что лучше сделать структуры неизменными.
В чем причина этого? Я вижу множество созданных Microsoft структур, которые могут изменяться, например, в xna. Вероятно, есть еще много в BCL.
Каковы плюсы и минусы несоблюдения этого правила?
9 ответов
Структуры должны представлять ценности. Значения не меняются. Число 12 вечно.
Однако учтите:
Foo foo = new Foo(); // a mutable struct
foo.Bar = 27;
Foo foo2 = foo;
foo2.Bar = 55;
Теперь foo.Bar и foo2.Bar отличаются, что часто бывает неожиданным. Особенно в таких случаях, как свойства (к счастью, компилятор обнаруживает это). Но также коллекции и т. Д.; как ты их разумно мутируешь?
Потеря данных слишком легко с изменяемой структурой.
Большим минусом является то, что вещи ведут себя не так, как вы ожидаете, особенно если изменчивость происходит из смеси прямого значения и ссылочного типа внутри него.
Честно говоря, я не могу вспомнить из головы все странные проблемы, с которыми я сталкивался, когда люди появлялись в группах новостей, когда они использовали изменяемые структуры - но эти причины, безусловно, существуют. Изменчивые структуры вызывают проблемы. Держись подальше.
РЕДАКТИРОВАТЬ: Я только что нашел электронное письмо, которое я написал некоторое время назад на эту тему. Это развивает только немного:
Это философски неправильно: структура должна представлять какую-то фундаментальную ценность. Те в основном неизменны. Вы не можете изменить число 5. Вы можете изменить значение переменной с 5 на 6, но вы не можете логически изменить само значение.
Это практически проблема: это создает много странных ситуаций. Это особенно плохо, если он изменчив через интерфейс. Затем вы можете начать изменять коробочные значения. Ик. Я видел много сообщений в новостных группах, которые связаны с тем, что люди пытаются использовать изменяемые структуры и сталкиваются с проблемами. Я видел очень странный пример LINQ, который терпел неудачу, потому что
List<T>.Enumerator
это структура, например.
Я часто использую изменяемые структуры в моем (критичном к производительности) проекте - и у меня нет проблем, потому что я понимаю смысл семантики копирования. Насколько я могу судить, главная причина, по которой люди защищают неизменные структуры, состоит в том, что люди, которые не понимают последствий, не могут попасть в неприятности.
Это не такая уж ужасная вещь - но сейчас мы находимся в опасности, когда она станет "истиной Евангелия", хотя на самом деле бывают времена, когда на законных основаниях это лучший вариант сделать структуру изменчивой. Как и во всем, есть исключения из правил.
Нет ничего дешевле, чем манипулировать изменяемой структурой, поэтому вы часто видите ее в высокопроизводительном коде, таком как процедуры обработки графики.
К сожалению, изменяемые структуры плохо работают с объектами и свойствами, слишком легко изменить копию структуры вместо самой структуры. Таким образом, они не подходят для большей части вашего кода.
PS Чтобы избежать затрат на копирование изменяемых структур, они обычно хранятся и передаются в виде массивов.
Техническая причина в том, что изменчивые структуры, по-видимому, способны делать то, чего на самом деле не делают. Так как семантика времени разработки такая же, как и у ссылочных типов, это становится непонятным для разработчиков. Этот код:
public void DoSomething(MySomething something)
{
something.Property = 10;
}
Ведет себя по-разному в зависимости от того, если MySomething
это struct
или class
, Для меня это убедительная, но не самая веская причина. Если вы посмотрите на Value Value объекта DDD, вы увидите связь с тем, как должны обрабатываться структуры. Объект значения в DDD может быть лучше всего представлен как тип значения в.Net (и, следовательно, структура). Поскольку у него нет идентичности, оно не может измениться.
Думайте об этом с точки зрения чего-то вроде вашего адреса. Вы можете "изменить" свой адрес, но сам адрес не изменился. На самом деле, вам назначен новый адрес. Концептуально это работает, потому что если вы действительно изменили свой адрес, ваши соседи по комнате тоже должны были бы переехать.
Вы спрашивали о плюсах и минусах несоблюдения руководящих принципов, которые должны быть неизменными.
Минусы: Минусы хорошо описаны в существующих ответах, и большинство описанных проблем связано с одной и той же причиной -неожиданным поведением из-за семантики значений структур.
Плюсы: Основным преимуществом использования изменяемых структур может бытьпроизводительность. Очевидно, что этот совет сопровождается всеми обычными предостережениями об оптимизации: убедитесь, что часть вашего кода нуждается в оптимизации, и убедитесь, что любые изменения действительно оптимизируют производительность вашего кода с помощью профилирования.
Для отличной статьи, обсуждающей, когда вы можете захотеть использовать изменяемые структуры, см. Викторину производительности Рико Мариани по программированию на основе значений (или, точнее, ответы).
Структура, как правило, должна представлять единственное некое единство. Таким образом, не имеет смысла изменять одно из свойств значения, имеет больше смысла создавать совершенно новое значение, если вы хотите, чтобы значение каким-то образом отличалось.
Семантика становится проще при использовании неизменяемых структур, и вы избегаете ловушек, подобных этой:
// a struct
struct Interval {
int From { get; set; }
int To { get; set; }
}
// create a list of structs
List<Interval> intervals = new List<Interval>();
// add a struct to the list
intervals.Add(new Interval());
// try to set the values of the struct
intervals[0].From = 10;
intervals[0].To = 20;
В результате структура в списке вообще не изменяется. Выражение Interval[0] копирует значение структуры из списка, затем вы изменяете свойство временного значения, но значение никогда не помещается обратно в список.
Изменить: Изменен пример использования списка вместо массива.
Когда вы копируете структуры вокруг себя, вы копируете их содержимое, поэтому, если вы изменяете скопированную версию, "оригинал" не будет обновляться.
Это источник ошибок, поскольку даже если вы знаете, что попадаете в ловушку копирования структуры (просто передавая ее в метод) и изменяя копию.
Просто случилось со мной снова на прошлой неделе, заставил меня час искать ошибку.
Сохранение неизменных структур предотвращает это...
Кроме этого вам нужно убедиться, что у вас есть действительно веская причина использовать структуры в первую очередь - "оптимизация" или "я хочу что-то, что быстро выделяется в стеке", не считается ответом. Маршалинг или другие вещи, от которых вы зависите от макета - хорошо, но обычно вы не должны хранить эти структуры слишком долго, они являются ценностями, а не объектами.
Причина, по которой вы должны сделать структуры неизменяемыми, заключается в том, что они являются типами ValueTypes, то есть они копируются каждый раз, когда вы передаете их методу.
Так, если, например, у вас есть свойство, которое возвращает структуру, изменение значения поля в этой структуре будет бесполезным, потому что получатель вернет копию структуры, а не ссылку на структуру. Я видел, как это делается в коде, и это часто трудно поймать.
Если вы разрабатываете свои структуры для неизменности, вы помогаете программисту избежать этих ошибок.