Подтвердить что ты не робот

Почему структуры С# неизменяемы?

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

"Проблема" с structs заключается в том, что когда метод (включая реализацию свойства) вызывается в структуре в контексте только для чтения (или неизменяемом местоположении), система копирует структуру, выполняет метод на временной копии, и молча отбрасывает результат. Это привело к тому, что программисты выдвинули несчастливое мнение о том, что способ избежать проблем с использованием методов mutating заключается в том, что многие структуры не разрешают кусочно-обновления, когда проблем можно было бы избежать, просто заменив свойства открытыми полями.

Кстати, некоторые люди жалуются, что когда свойство класса возвращает удобно изменяемую структуру, изменения в структуре не влияют на класс, из которого он пришел. Я бы сказал, что хорошая вещь - тот факт, что возвращенный элемент является структурой, делает поведение понятным (особенно, если это структура с открытым полем). Сравните фрагмент, используя гипотетическую структуру и свойство на 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 не та.

Ответ 4

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

Ответ 5

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

Уменяемые структуры порождают ошибки.