Почему структуры C# неизменяемы?
Мне было просто интересно узнать, почему структуры, строки и т. Д. Неизменны? Что является причиной того, что они становятся неизменяемыми, а остальные объекты изменяемыми? Какие вещи считаются объектами неизменными?
Есть ли разница в том, как выделяется и освобождается память для изменяемых и неизменяемых объектов?
5 ответов
Если эта тема вас интересует, у меня есть несколько статей об неизменном программировании на http://blogs.msdn.com/b/ericlippert/archive/tags/immutability/
Мне было просто интересно узнать, почему структуры, строки и т. Д. Неизменны?
Структуры и классы не являются неизменяемыми по умолчанию, хотя рекомендуется сделать структуры неизменяемыми. Мне тоже нравятся неизменные занятия.
Строки неизменны.
Что является причиной того, что они становятся неизменяемыми, а остальные объекты изменяемыми?
Причины сделать все типы неизменяемыми:
Проще рассуждать об объектах, которые не меняются. Если у меня есть очередь с тремя элементами, я знаю, что она не пуста сейчас, она не была пустой пять минут назад, она не будет пустой в будущем. Это неизменно! Как только я узнаю факт об этом, я могу использовать этот факт навсегда. Факты о неизменных объектах не устаревают.
Особый случай первого пункта: неизменяемые объекты намного проще сделать потокобезопасными. Большинство проблем безопасности потока связано с записью в одном потоке и чтением в другом; неизменные объекты не имеют записи.
Неизменяемые объекты могут быть разобраны и использованы повторно. Например, если у вас есть неизменяемое двоичное дерево, вы можете использовать его левое и правое поддеревья в качестве поддеревьев другого дерева, не беспокоясь об этом. В изменчивой структуре вы обычно создаете копии данных для повторного использования, потому что не хотите, чтобы изменения одного логического объекта влияли на другой. Это может сэкономить много времени и памяти.
Причины сделать структуры неизменными
Есть много причин, чтобы сделать структуры неизменяемыми. Вот только один.
Структуры копируются по значению, а не по ссылке. Легко случайно обработать структуру как скопированную по ссылке. Например:
void M()
{
S s = whatever;
... lots of code ...
s.Mutate();
... lots more code ...
Console.WriteLine(s.Foo);
...
}
Теперь вы хотите преобразовать часть этого кода во вспомогательный метод:
void Helper(S s)
{
... lots of code ...
s.Mutate();
... lots more code ...
}
НЕПРАВИЛЬНО! Это должно быть (ref S s) - если вы этого не сделаете, то мутация произойдет на копии s. Если вы не допустите мутации, то все эти проблемы исчезнут.
Причины сделать строки неизменяемыми
Помните мое первое замечание о фактах, касающихся неизменных структур?
Предположим, что строка была изменяемой:
public static File OpenFile(string filename)
{
if (!HasPermission(filename)) throw new SecurityException();
return InternalOpenFile(filename);
}
Что если враждебный абонент изменяет имя файла после проверки безопасности и перед открытием файла? Код только что открыл файл, на который у них может не быть разрешения!
Опять же, изменяемые данные трудно рассуждать. Вы хотите, чтобы тот факт, что "этот вызывающий пользователь имеет право видеть файл, описываемый этой строкой", будет истинным всегда, пока не произойдет мутация. С изменяемыми строками, чтобы писать безопасный код, нам постоянно приходилось делать копии данных, которые, как мы знаем, не меняются.
Какие вещи считаются объектами неизменными?
Представляет ли тип логически что-то, что является "вечным" значением? Число 12 - это число 12; это не меняется. Целые числа должны быть неизменными. Точка (10, 30) является точкой (10, 30); это не меняется. Очки должны быть неизменными. Строка "abc" является строкой "abc"; это не меняется. Строки должны быть неизменными. Список (10, 20, 30) не меняется. И так далее.
Иногда тип представляет вещи, которые действительно меняются. Фамилия Мэри Смит - Смит, но завтра она может быть Мэри Джонс. Или мисс Смит сегодня может стать доктором Смитом завтра. У пришельца теперь пятьдесят очков здоровья, но у него десять очков после удара лазерным лучом. Некоторые вещи лучше всего представить как мутации.
Есть ли разница в том, как выделяется и освобождается память для изменяемых и неизменяемых объектов?
Не как таковой. Как я уже упоминал ранее, одна из приятных сторон неизменяемых ценностей состоит в том, что вы можете повторно использовать их части без копирования. Так что в этом смысле распределение памяти может быть очень разным.
Структуры не... вот почему изменчивые структуры являются злом.
Создание изменяемых структур может привести к всевозможному странному поведению в вашем приложении, и поэтому они считаются очень плохой идеей (вытекает из того факта, что они выглядят как ссылочный тип, но на самом деле являются типом значения и будут копироваться всякий раз, когда вы передаете их вокруг).
Строки, с другой стороны, есть. Это делает их по сути потокобезопасными, а также позволяет оптимизировать их через интернирование строк. Если вам нужно создать сложную строку на лету, вы можете использовать StringBuilder
,
Понятия изменчивости и неизменности имеют разные значения применительно к структурам и классам. Ключевым аспектом (часто ключевым недостатком) изменчивых классов является Foo
имеет поле Bar
типа List<Integer>
, который содержит ссылку на список, содержащий (1,2,3), другой код, который имеет ссылку на этот же список, может изменить его так, чтобы Bar
содержит ссылку на список, содержащий (4,5,6), даже если этот другой код не имеет никакого доступа кBar
, В отличие от этого, если Foo
было поле Biz
типа System.Drawing.Point
, единственный способ что-нибудь изменить Biz
будет иметь право на запись в это поле.
Поля (открытые и закрытые) структуры могут быть изменены любым кодом, который может видоизменить область хранения, в которой хранится структура, и не могут быть изменены каким-либо кодом, который не может изменить место хранения, в котором она хранится. Если вся информация, инкапсулированная в структуре, хранится в ее полях, такая структура может эффективно объединить управление неизменяемым типом с удобством изменяемого типа, если структура не закодирована таким образом, чтобы устранить такое удобство (привычка, которую, к сожалению, рекомендуют некоторые программисты Microsoft).
"Проблема" со структурами заключается в том, что когда метод (включая реализацию свойства) вызывается для структуры в контексте только для чтения (или неизменяемого расположения), система копирует структуру, выполняет метод для временной копии и молча отбрасывает результат. Такое поведение привело к тому, что программисты выдвинули досадное мнение о том, что способ избежать проблем с изменяющимися методами состоит в том, чтобы многие структуры запрещали кусочные обновления, когда проблем можно было бы избежать, просто заменив свойства открытыми полями.
Кстати, некоторые люди жалуются, что когда свойство класса возвращает удобно изменяемую структуру, изменения в структуре не влияют на класс, из которого оно получено. Я бы сказал, что это хорошо - тот факт, что возвращаемый элемент является структурой, проясняет поведение (особенно, если это структура с открытыми полями). Сравните фрагмент с использованием гипотетической структуры и свойства на Drawing.Matrix
с использованием фактического свойства этого класса, реализованного Microsoft:
// Гипотетическая структура публичная структура { публичное размещение xx,xy,yx,yy,dx,dy; } Transform2d; // Гипотетическое свойство "System.Drawing.Drawing2d.Matrix" public Transform2d Transform {get;} // Фактическое свойство "System.Drawing.Drawing2d.Matrix" public float[] Elements { get; } // Код с использованием гипотетической структуры Transform2d myTransform = myMatrix.Transform; myTransform.dx += 20; ... другой код, использующий myTransform // Код с использованием фактического свойства Microsoft float[] myArray = myMatrix.Elements; myArray[4] += 20; ... другой код, использующий myArray
Глядя на фактическое свойство Microsoft, есть ли способ сказать, является ли запись в myArray[4]
повлияет myMatrix
? Даже глядя на страницу http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.matrix.elements.aspx есть ли способ сказать? Если бы свойство было написано с использованием структурного эквивалента, не было бы никакой путаницы; свойство, которое возвращает структуру, не вернет ни больше, ни меньше, чем текущее значение шести чисел. изменения myTransform.dx
было бы не чем иным, как записью в переменную с плавающей точкой, которая не была привязана ни к чему другому. Тот, кому не нравится тот факт, что меняется myTransform.dx
не влияет myMatrix
должно быть одинаково раздражен тем, что написание myArray[4]
не влияет myMatrix
либо, кроме того, что независимость myMatrix
а также myTransform
очевидно, в то время как независимость myMatrix
а также myArray
не является.
Тип структуры не является неизменным. Да, строки есть. Сделать свой собственный тип неизменяемым легко, просто не предоставляйте конструктор по умолчанию, делайте все поля приватными и не определяйте методы или свойства, которые изменяют значение поля. Иметь метод, который должен мутировать объект, вместо этого возвращать новый объект. Есть угол управления памятью, вы склонны создавать много копий и мусора.
Структуры могут быть изменяемыми, но это плохая идея, потому что они имеют семантику копирования. Если вы вносите изменения в структуру, вы можете изменять копию. Отслеживать, что именно было изменено, очень сложно.
Изменчивые структуры порождают ошибки.