Почему структуры C # неизменяемы? - PullRequest
54 голосов
/ 20 сентября 2010

Мне было просто интересно узнать, почему структуры, строки и т. Д. Неизменны?Что является причиной того, что они становятся неизменяемыми, а остальные объекты изменяемыми?Какие вещи делают объект неизменным?

Есть ли разница в том, как выделяется и освобождается память для изменяемых и неизменных объектов?

Ответы [ 5 ]

114 голосов
/ 20 сентября 2010

Если эта тема вас интересует, у меня есть ряд статей об неизменяемом программировании на 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) не меняется. И так далее.

Иногда тип представляет вещи, которые действительно меняются. Фамилия Мэри Смит - Смит, но завтра она может быть Мэри Джонс. Или мисс Смит сегодня может быть доктором Смитом завтра. У пришельца теперь пятьдесят очков здоровья, но у него десять очков после удара лазерным лучом. Некоторые вещи лучше всего представить как мутации.

Есть ли разница в том, как выделяется и освобождается память для изменяемых и неизменяемых объектов?

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

9 голосов
/ 20 сентября 2010

Структуры не ... вот почему изменяемые структуры являются злом.

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

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

4 голосов
/ 10 октября 2012

Понятия изменчивости и неизменности имеют разные значения применительно к структурам и классам.Ключевой аспект (часто ключевой недостаток) изменяемых классов заключается в том, что Foo имеет поле Bar типа List<Integer>, которое содержит ссылку на список, содержащий (1,2,3), другой код, который имеетссылка на этот же список может изменить его, так что Bar содержит ссылку на список, содержащий (4,5,6), , даже если этот другой код не имеет доступа к Bar.Напротив, если бы Foo имело поле Biz типа System.Drawing.Point, единственным способом, которым что-либо могло изменить любой аспект Biz, было бы , чтобы иметь доступ на запись к этому полю .

Поля (открытые и закрытые) структуры могут быть изменены любым кодом, который может изменить местоположение хранилища, в котором хранится структура, и не могут быть изменены каким-либо кодом, который не может изменить местоположение хранилища, в котором он хранится.Если вся информация, инкапсулированная в структуре, содержится в ее полях, такая структура может эффективно комбинировать управление неизменяемым типом с удобством изменяемого типа, если структура не закодирована таким образом, чтобы устранить такое удобство (привычка, которую, к сожалению, рекомендуют некоторые программисты Microsoft).

«Проблема» со структурами заключается в том, что когда метод (включая реализацию свойства) вызывается для структуры в контексте только для чтения (или неизменном месте)), система копирует структуру, выполняет метод для временной копии и молча отбрасывает результат.Такое поведение привело к тому, что программисты выдвинули досадную идею о том, что способ избежать проблем с методами мутации состоит в том, чтобы многие структуры запрещали кусочные обновления, когда проблем можно было бы лучше избежать, просто просто заменив свойства открытыми полями .

Кстати, некоторые люди жалуются, что когда свойство класса возвращает удобно изменяемую структуру, изменения в структуре не влияют на класс, из которого оно получено.Я бы сказал, что это хорошо - тот факт, что возвращаемый элемент является структурой, делает поведение понятным (особенно, если это структура с открытыми полями).Сравните фрагмент с гипотетической структурой и свойством Drawing.Matrix с фрагментом, использующим фактическое свойство этого класса в Microsoft:

// Hypothetical struct
public struct {
  public float xx,xy,yx,yy,dx,dy;
} Transform2d;

// Hypothetical property of "System.Drawing.Drawing2d.Matrix"
public Transform2d Transform {get;}

// Actual property of "System.Drawing.Drawing2d.Matrix"
public float[] Elements { get; }

// Code using hypothetical struct
Transform2d myTransform = myMatrix.Transform;
myTransform.dx += 20;
... other code using myTransform

// Code using actual Microsoft property
float[] myArray = myMatrix.Elements;
myArray[4] += 20;
... other code using 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 не является.

2 голосов
/ 20 сентября 2010

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

1 голос
/ 20 сентября 2010

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

Изменчивые структуры порождают ошибки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...