Неизменяемый объект
В объектно-ориентированном (ОО) и функциональном программировании неизменяемый объект (неизменяемый) [1] object) — объект которого , состояние нельзя изменить после его создания. [2] В этом отличие от изменяемого объекта (изменяемого объекта), который можно изменить после его создания. [3] В некоторых случаях объект считается неизменным, даже если изменяются некоторые внутренние атрибуты, но состояние объекта кажется неизменным с внешней точки зрения. Например, объект, который использует мемоизацию для кэширования результатов дорогостоящих вычислений, по-прежнему может считаться неизменяемым объектом.
Строки и другие конкретные объекты обычно выражаются как неизменяемые объекты для улучшения читаемости и эффективности выполнения объектно-ориентированного программирования. Неизменяемые объекты также полезны, поскольку они по своей сути потокобезопасны . [2] Другие преимущества заключаются в том, что их легче понять и обосновать, а также они обеспечивают более высокий уровень безопасности, чем изменяемые объекты. [2]
Концепции [ править ]
Неизменяемые переменные [ править ]
В императивном программировании значения, хранящиеся в переменных программы , содержимое которых никогда не меняется, называются константами, чтобы отличать их от переменных, которые могут быть изменены во время выполнения. Примеры включают коэффициенты перевода метров в футы или значение числа Пи с точностью до нескольких десятичных знаков.
Поля, доступные только для чтения, могут рассчитываться во время работы программы (в отличие от констант, которые известны заранее), но никогда не изменяются после их инициализации.
Слабая и сильная неизменяемость [ править ]
Иногда говорят о неизменяемости определенных полей объекта. Это означает, что невозможно изменить эти части состояния объекта, даже если другие части объекта могут быть изменяемыми ( слабо неизменяемыми ). Если все поля неизменяемы, то объект является неизменяемым. Если весь объект не может быть расширен другим классом, объект называется строго неизменяемым . [4] Это может, например, помочь явно обеспечить соблюдение определенных инвариантов в отношении определенных данных в объекте, которые останутся неизменными на протяжении всего времени существования объекта. В некоторых языках это делается с помощью ключевого слова (например, const
в С++ , final
в Java ), который обозначает поле как неизменяемое. В некоторых языках все наоборот: в OCaml поля объекта или записи по умолчанию являются неизменяемыми и должны быть явно помечены значком mutable
быть таким.
Ссылки на объекты [ править ]
В большинстве объектно-ориентированных языков на объекты можно ссылаться с помощью ссылок . Некоторыми примерами таких языков являются Java , C++ , C# , VB.NET и многие языки сценариев , такие как Perl , Python и Ruby . В этом случае имеет значение, может ли состояние объекта меняться при совместном использовании объектов через ссылки.
Ссылки и копирование объектов [ править ]
Если известно, что объект является неизменяемым, предпочтительно создать ссылку на него, а не копировать весь объект. Это сделано для экономии памяти за счет предотвращения дублирования данных и избежания вызовов конструкторов и деструкторов; это также приводит к потенциальному увеличению скорости выполнения.
Технику копирования ссылок гораздо сложнее использовать для изменяемых объектов, поскольку если какой-либо пользователь ссылки на изменяемый объект меняет ее, все остальные пользователи этой ссылки увидят это изменение. Если это не ожидаемый эффект, может быть сложно уведомить других пользователей о том, чтобы они отреагировали правильно. В таких ситуациях защитное копирование всего объекта, а не ссылки, обычно является простым, но дорогостоящим решением. Шаблон наблюдателя — это альтернативный метод обработки изменений изменяемых объектов.
Копирование при записи [ править ]
сочетает в себе преимущества изменяемых и неизменяемых объектов и поддерживается непосредственно практически всем современным оборудованием Метод копирования при записи (COW) . При использовании этого метода, когда пользователь просит систему скопировать объект, она вместо этого просто создает новую ссылку, которая по-прежнему указывает на тот же объект. Как только пользователь пытается изменить объект с помощью определенной ссылки, система создает реальную копию, применяет к ней изменения и устанавливает ссылку для ссылки на новую копию. На других пользователей это не влияет, поскольку они по-прежнему ссылаются на исходный объект. Таким образом, в COW все пользователи имеют изменяемую версию своих объектов, хотя в случае, если пользователи не изменяют свои объекты, преимущества неизменяемых объектов в экономии места и скорости сохраняются. Копирование при записи популярно в системах виртуальной памяти , поскольку позволяет им экономить пространство памяти, сохраняя при этом правильную обработку всего, что может делать прикладная программа.
Стажировка [ править ]
Практика постоянного использования ссылок вместо копий одинаковых объектов называется интернированием . Если используется интернирование, два объекта считаются равными тогда и только тогда, когда их ссылки, обычно представленные в виде указателей или целых чисел, равны. Некоторые языки делают это автоматически: например, Python автоматически интернирует короткие строки . Если алгоритм, реализующий интернирование, гарантированно делает это во всех возможных случаях, то сравнение объектов на равенство сводится к сравнению их указателей — существенный выигрыш в скорости в большинстве приложений. (Даже если не гарантируется полнота алгоритма, все равно существует возможность улучшения быстрого пути , когда объекты равны и используют одну и ту же ссылку.) Интернирование обычно полезно только для неизменяемых объектов.
Потокобезопасность [ править ]
Неизменяемые объекты могут быть полезны в многопоточных приложениях. Несколько потоков могут работать с данными, представленными неизменяемыми объектами, не беспокоясь об изменении данных другими потоками. Поэтому неизменяемые объекты считаются более потокобезопасными, чем изменяемые объекты.
Нарушение неизменности [ править ]
компьютера, Неизменяемость не означает, что объект, хранящийся в памяти не подлежит записи. Скорее, неизменяемость — это конструкция времени компиляции , которая указывает, что программист может сделать через обычный интерфейс объекта, а не обязательно то, что он может сделать абсолютно (например, обходя систему типов или нарушая корректность const в C или C++ ).
Подробности, специфичные для языка [ править ]
На Python , Java [5] : 80 и .NET Framework строки являются неизменяемыми объектами. И в Java, и в .NET Framework есть изменяемые версии строк. На Яве [5] : 84 это StringBuffer
и StringBuilder
(изменяемые версии Java String
), а в .NET это StringBuilder
(изменяемая версия .Net String
). Python 3 имеет вариант изменяемой строки (байтов) с именем bytearray
. [6]
Кроме того, все примитивные классы-оболочки в Java являются неизменяемыми.
Похожие шаблоны — Immutable Interface и Immutable Wrapper .
В чисто функциональных языках программирования невозможно создавать изменяемые объекты без расширения языка (например, с помощью библиотеки изменяемых ссылок или внешнего интерфейса функций ), поэтому все объекты являются неизменяемыми.
Есть [ править ]
В Ada любой объект объявляется либо переменной (т. е. изменяемой; обычно это неявное значение по умолчанию), либо constant
(т.е. неизменяемый) через constant
ключевое слово.
type Some_type is new Integer; -- could be anything more complicated
x: constant Some_type:= 1; -- immutable
y: Some_type; -- mutable
Параметры подпрограммы неизменяемы в режиме in и изменяемы в режимах in out и out .
procedure Do_it(a: in Integer; b: in out Integer; c: out Integer) is
begin
-- a is immutable
b:= b + a;
c:= a;
end Do_it;
С# [ править ]
В C# вы можете обеспечить неизменность полей класса с помощью readonly
заявление. [7] : 239
Сделав все поля неизменяемыми, вы получите неизменяемый тип.
class AnImmutableType
{
public readonly double _value;
public AnImmutableType(double x)
{
_value = x;
}
public AnImmutableType Square()
{
return new AnImmutableType(_value * _value);
}
}
В C# есть неизменяемые записи. [8] [9]
record Person(string FirstName, string LastName);
С++ [ править ]
В C++ константно-корректная реализация Cart
позволит пользователю создавать экземпляры класса, а затем использовать их как const
(неизменяемый) или изменяемый, по желанию, предоставляя две разные версии items()
метод. (Обратите внимание, что в C++ нет необходимости — и фактически невозможно — предоставлять специализированный конструктор для const
случаи.)
class Cart {
public:
Cart(std::vector<Item> items): items_(items) {}
std::vector<Item>& items() { return items_; }
const std::vector<Item>& items() const { return items_; }
int ComputeTotalCost() const { /* return sum of the prices */ }
private:
std::vector<Item> items_;
};
Обратите внимание: если существует элемент данных, который является указателем или ссылкой на другой объект, то изменить объект, на который указывает или на который ссылается ссылка, можно только внутри неконстантного метода.
C++ также обеспечивает абстрактную (в отличие от побитовой) неизменяемость посредством mutable
ключевое слово, которое позволяет изменять переменную-член изнутри const
метод.
class Cart {
public:
Cart(std::vector<Item> items): items_(items) {}
const std::vector<Item>& items() const { return items_; }
int ComputeTotalCost() const {
if (total_cost_) {
return *total_cost_;
}
int total_cost = 0;
for (const auto& item : items_) {
total_cost += item.Cost();
}
total_cost_ = total_cost;
return total_cost;
}
private:
std::vector<Item> items_;
mutable std::optional<int> total_cost_;
};
Д [ править ]
В D существуют два квалификатора типа : const
и immutable
, для переменных, которые нельзя изменить. [10] В отличие от C++ const
, Ява final
и C# readonly
, они транзитивны и рекурсивно применяются ко всему, что достижимо через ссылки на такую переменную. Разница между const
и immutable
это то, к чему они применяются: const
является свойством переменной: на указанное значение могут легально существовать изменяемые ссылки, т. е. значение может фактически измениться. В отличие, immutable
является свойством указанного значения: значение и все, что транзитивно достижимо из него, не может измениться (без нарушения системы типов, что приводит к неопределенному поведению ). Любая ссылка на это значение должна быть отмечена const
или immutable
. В основном для любого неквалифицированного типа T
, const(T)
представляет собой непересекающийся союз T
(изменяемый) и immutable(T)
.
class C {
/*mutable*/ Object mField;
const Object cField;
immutable Object iField;
}
Для изменяемого C
объект, его mField
можно написать. Для const(C)
объект, mField
не может быть изменен, он наследует const
; iField
по-прежнему неизменен, поскольку это более сильная гарантия. Для immutable(C)
, все поля неизменяемы.
В такой функции:
void func(C m, const C c, immutable C i)
{ /* inside the braces */ }
Внутри брекетов, c
может относиться к тому же объекту, что и m
, поэтому мутации в m
может косвенно изменить c
также. Также,
c
может относиться к тому же объекту, что и i
, но поскольку значение тогда является неизменным, изменений не происходит. Однако, m
и i
не могут по закону относиться к одному и тому же объекту.
На языке гарантий mutable не имеет никаких гарантий (функция может изменить объект), const
является внешней гарантией того, что функция ничего не изменит, и
immutable
является двунаправленной гарантией (функция не изменит значение, и вызывающая сторона не должна его менять).
Ценности, которые const
или immutable
должен быть инициализирован прямым присвоением в точке объявления или конструктором .
Потому что const
параметры забывают, было ли значение изменяемым или нет, аналогичная конструкция, inout
, действует в некотором смысле как переменная для информации об изменчивости.
Функция типа const(S) function(const(T))
возвращает const(S)
типизированные значения для изменяемых, константных и неизменяемых аргументов. Напротив, функция типа inout(S) function(inout(T))
возвращает S
для изменяемого T
аргументы, const(S)
для const(T)
ценности и immutable(S)
для immutable(T)
ценности.
Приведение неизменяемых значений к изменяемым приводит к неопределенному поведению при изменении, даже если исходное значение происходит из изменяемого источника. Приведение изменяемых значений к неизменяемым может быть законным, если после этого не останется изменяемых ссылок. «Выражение может быть преобразовано из изменяемого (...) в неизменяемое, если выражение уникально и все выражения, на которые оно транзитивно ссылается, либо уникальны, либо неизменяемы». [10] Если компилятор не может доказать уникальность, приведение можно выполнить явно, и программист должен гарантировать отсутствие изменяемых ссылок.
Тип string
это псевдоним для immutable(char)[]
, т.е. типизированный фрагмент памяти неизменяемых символов. [11] Создание подстрок обходится дешево, поскольку просто копирует и изменяет указатель и поле длины, и безопасно, поскольку базовые данные не могут быть изменены. Объекты типа const(char)[]
может относиться к строкам, а также к изменяемым буферам.
Создание поверхностной копии константного или неизменяемого значения удаляет внешний слой неизменяемости: копирование неизменяемой строки ( immutable(char[])
) возвращает строку ( immutable(char)[]
). Неизменяемый указатель и длина копируются, а копии изменяемы. Указанные данные не были скопированы и сохраняют свой квалификатор, в примере immutable
. Его можно удалить, создав глубокую копию, например, с помощью dup
функция.
Ява [ править ]
Классическим примером неизменяемого объекта является экземпляр класса Java. String
сорт
String s = "ABC";
s.toLowerCase(); // This accomplishes nothing!
Метод toLowerCase()
не меняет данные «ABC», которые s
содержит. Вместо этого создается экземпляр нового объекта String, которому во время создания присваиваются данные «abc». Ссылка на этот объект String возвращается методом toLowerCase()
метод. Чтобы сделать строку s
содержат данные «abc», необходим другой подход:
s = s.toLowerCase();
Теперь строка s
ссылается на новый объект String, содержащий «abc». В синтаксисе объявления класса String нет ничего, что делало бы его неизменяемым; скорее, ни один из методов класса String никогда не влияет на данные, содержащиеся в объекте String, что делает его неизменяемым.
Ключевое слово final
( подробная статья ) используется при реализации неизменяемых примитивных типов и ссылок на объекты, [12] но он не может сам по себе сделать сами объекты неизменными. См. примеры ниже:
Переменные примитивного типа ( int
, long
, short
и т. д.) можно переназначить после определения. Этого можно избежать, используя final
.
int i = 42; //int is a primitive type
i = 43; // OK
final int j = 42;
j = 43; // does not compile. j is final so can't be reassigned
Ссылочные типы нельзя сделать неизменяемыми, просто используя final
ключевое слово. final
только предотвращает переназначение.
final MyObject m = new MyObject(); //m is of reference type
m.data = 100; // OK. We can change state of object m (m is mutable and final doesn't change this fact)
m = new MyObject(); // does not compile. m is final so can't be reassigned
Примитивные обертки ( Integer
, Long
, Short
, Double
, Float
, Character
, Byte
, Boolean
) также все неизменяемы. Неизменяемые классы можно реализовать, следуя нескольким простым рекомендациям. [13]
JavaScript [ править ]
В JavaScript все примитивные типы (Undefine, Null, Boolean, Number, BigInt, String, Symbol) являются неизменяемыми, но пользовательские объекты обычно изменяемы.
function doSomething(x) { /* does changing x here change the original? */ };
var str = 'a string';
var obj = { an: 'object' };
doSomething(str); // strings, numbers and bool types are immutable, function gets a copy
doSomething(obj); // objects are passed in by reference and are mutable inside function
doAnotherThing(str, obj); // `str` has not changed, but `obj` may have.
Чтобы имитировать неизменность объекта, можно определить свойства как доступные только для чтения (доступные для записи: false).
var obj = {};
Object.defineProperty(obj, 'foo', { value: 'bar', writable: false });
obj.foo = 'bar2'; // silently ignored
Однако описанный выше подход по-прежнему позволяет добавлять новые свойства. В качестве альтернативы можно использовать Object.freeze , чтобы сделать существующие объекты неизменяемыми.
var obj = { foo: 'bar' };
Object.freeze(obj);
obj.foo = 'bars'; // cannot edit property, silently ignored
obj.foo2 = 'bar2'; // cannot add property, silently ignored
Благодаря реализации ECMA262 JavaScript получил возможность создавать неизменяемые ссылки, которые нельзя переназначить. Однако, используя const
Объявление не означает, что значение ссылки только для чтения является неизменяемым, просто имя не может быть присвоено новому значению.
const ALWAYS_IMMUTABLE = true;
try {
ALWAYS_IMMUTABLE = false;
} catch (err) {
console.log("Can't reassign an immutable reference.");
}
const arr = [1, 2, 3];
arr.push(4);
console.log(arr); // [1, 2, 3, 4]
Использование неизменяемого состояния стало растущей тенденцией в JavaScript с момента появления React , который отдает предпочтение шаблонам управления состоянием, подобным Flux, таким как Redux . [14]
Перл [ править ]
В Perl можно создать неизменяемый класс с помощью библиотеки Moo, просто объявив все атрибуты только для чтения:
package Immutable;
use Moo;
has value => (
is => 'ro', # read only
default => 'data', # can be overridden by supplying the constructor with
# a value: Immutable->new(value => 'something else');
);
1;
Раньше создание неизменяемого класса требовало двух шагов: во-первых, создание средств доступа (автоматически или вручную), которые предотвращают изменение атрибутов объекта, и, во-вторых, предотвращение прямого изменения данных экземпляра экземпляров этого класса (обычно это хранилось в хэше). ссылку и может быть заблокирован с помощью функции lock_hash Hash::Util):
package Immutable;
use strict;
use warnings;
use base qw(Class::Accessor);
# create read-only accessors
__PACKAGE__->mk_ro_accessors(qw(value));
use Hash::Util 'lock_hash';
sub new {
my $class = shift;
return $class if ref($class);
die "Arguments to new must be key => value pairs\n"
unless (@_ % 2 == 0);
my %defaults = (
value => 'data',
);
my $obj = {
%defaults,
@_,
};
bless $obj, $class;
# prevent modification of the object data
lock_hash %$obj;
}
1;
Или с помощью написанного вручную аксессора:
package Immutable;
use strict;
use warnings;
use Hash::Util 'lock_hash';
sub new {
my $class = shift;
return $class if ref($class);
die "Arguments to new must be key => value pairs\n"
unless (@_ % 2 == 0);
my %defaults = (
value => 'data',
);
my $obj = {
%defaults,
@_,
};
bless $obj, $class;
# prevent modification of the object data
lock_hash %$obj;
}
# read-only accessor
sub value {
my $self = shift;
if (my $new_value = shift) {
# trying to set a new value
die "This object cannot be modified\n";
} else {
return $self->{value}
}
}
1;
Питон [ править ]
В Python некоторые встроенные типы (числа, логические значения, строки, кортежи, замороженные наборы) являются неизменяемыми, но пользовательские классы, как правило, изменяемы. Чтобы имитировать неизменяемость в классе, можно переопределить установку и удаление атрибута, чтобы вызвать исключения:
class ImmutablePoint:
"""An immutable class with two attributes 'x' and 'y'."""
__slots__ = ['x', 'y']
def __setattr__(self, *args):
raise TypeError("Can not modify immutable instance.")
__delattr__ = __setattr__
def __init__(self, x, y):
# We can no longer use self.value = value to store the instance data
# so we must explicitly call the superclass
super().__setattr__('x', x)
super().__setattr__('y', y)
Помощники стандартной библиотеки collections.namedtuple
и typing.NamedTuple
, доступный начиная с Python 3.6, позволяет создавать простые неизменяемые классы. Следующий пример примерно эквивалентен приведенному выше, плюс некоторые функции, подобные кортежу:
from typing import NamedTuple
import collections
Point = collections.namedtuple('Point', ['x', 'y'])
# the following creates a similar namedtuple to the above
class Point(NamedTuple):
x: int
y: int
Представлено в Python 3.7, dataclasses
позволяют разработчикам эмулировать неизменяемость с помощью замороженных экземпляров . Если создается замороженный класс данных, dataclasses
переопределит __setattr__()
и __delattr__()
поднять FrozenInstanceError
если его вызвать.
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
Ракетка [ править ]
Racket существенно отличается от других реализаций Scheme , делая тип базовой пары («минус-ячейки») неизменяемым. Вместо этого он предоставляет тип параллельной изменяемой пары через mcons
, mcar
, set-mcar!
и т. д. Кроме того, поддерживаются многие неизменяемые типы, например неизменяемые строки и векторы, и они широко используются. Новые структуры по умолчанию являются неизменяемыми, если только поле специально не объявлено изменяемым или вся структура:
(struct foo1 (x y)) ; all fields immutable
(struct foo2 (x [y #:mutable])) ; one mutable field
(struct foo3 (x y) #:mutable) ; all fields mutable
Язык также поддерживает функционально реализованные неизменяемые хеш-таблицы и неизменяемые словари.
Ржавчина [ править ]
Rust Система владения позволяет разработчикам объявлять неизменяемые переменные и передавать неизменяемые ссылки. По умолчанию все переменные и ссылки неизменяемы. Изменяемые переменные и ссылки создаются явно с помощью mut
ключевое слово.
Постоянные элементы в Rust всегда неизменяемы.
// constant items are always immutable
const ALWAYS_IMMUTABLE: bool = true;
struct Object {
x: usize,
y: usize,
}
fn main() {
// explicitly declare a mutable variable
let mut mutable_obj = Object { x: 1, y: 2 };
mutable_obj.x = 3; // okay
let mutable_ref = &mut mutable_obj;
mutable_ref.x = 1; // okay
let immutable_ref = &mutable_obj;
immutable_ref.x = 3; // error E0594
// by default, variables are immutable
let immutable_obj = Object { x: 4, y: 5 };
immutable_obj.x = 6; // error E0596
let mutable_ref2 =
&mut immutable_obj; // error E0596
let immutable_ref2 = &immutable_obj;
immutable_ref2.x = 6; // error E0594
}
Масштаб [ править ]
В Scala любую сущность (в узком смысле, привязку) можно определить как изменяемую или неизменяемую: в объявлении можно использовать val
(значение) для неизменяемых сущностей и var
(переменная) для изменяемых. Обратите внимание: даже если неизменяемую привязку нельзя переназначить, она все равно может ссылаться на изменяемый объект, и для этого объекта по-прежнему можно вызывать изменяющие методы: привязка является неизменной, но базовый объект может быть изменяемым.
Например, следующий фрагмент кода:
val maxValue = 100
var currentValue = 1
определяет неизменяемую сущность maxValue
(целочисленный тип определяется во время компиляции) и изменяемый объект с именем currentValue
.
По умолчанию классы коллекций, такие как List
и Map
являются неизменяемыми, поэтому методы обновления возвращают новый экземпляр, а не изменяют существующий. Хотя это может показаться неэффективным, реализация этих классов и их гарантии неизменности означают, что новый экземпляр может повторно использовать существующие узлы, что, особенно в случае создания копий, очень эффективно. [15] [ нужен лучший источник ]
См. также [ править ]
Ссылки [ править ]
Эта статья содержит некоторые материалы из Книги шаблонов проектирования Perl.
- ^ «неизменяемое прилагательное — определение, изображения, произношение и примечания по использованию — Оксфордский словарь для продвинутых учащихся на сайте OxfordLearnersDictionaries.com» . www.oxfordlearnersdictionaries.com .
- ↑ Перейти обратно: Перейти обратно: а б с Гетц и др. Параллелизм Java на практике . Addison Wesley Professional, 2006, раздел 3.4. Неизменяемость
- ^ «6.005 — Создание программного обеспечения» .
- ^ Дэвид О'Мира (апрель 2003 г.). «Изменяемые и неизменяемые объекты: убедитесь, что методы не могут быть переопределены» . Яванское ранчо . Проверено 14 мая 2012 г.
Предпочтительный способ — сделать класс финальным. Иногда это называют «сильной неизменностью». Это не позволяет кому-либо расширить ваш класс и случайно или намеренно сделать его изменяемым.
- ↑ Перейти обратно: Перейти обратно: а б Блох, Джошуа (2018). «Эффективная Java: Руководство по языку программирования» (третье изд.). Аддисон-Уэсли. ISBN 978-0134685991 .
- ^ «Встроенные функции — документация Python v3.0» . docs.python.org .
- ^ Скит, Джон (23 марта 2019 г.). C# в глубине . Мэннинг. ISBN 978-1617294532 .
- ^ «Использование типов записей — учебник C# — C#» . Learn.microsoft.com . 14 ноября 2023 г. Проверено 23 февраля 2024 г.
- ^ «Записи — Справочник по C# — C#» . Learn.microsoft.com . 25 мая 2023 г. Проверено 23 февраля 2024 г.
- ↑ Перейти обратно: Перейти обратно: а б D Спецификация языка § 18
- ^ D Спецификация языка § 12.16 (Термины массив и срез используются как взаимозаменяемые.)
- ^ «Как создать неизменяемый класс и объект в Java – пример учебника» . Javarevisited.blogspot.co.uk. 04.03.2013 . Проверено 14 апреля 2014 г.
- ^ «Неизменяемые объекты» . javapractices.com . Проверено 15 ноября 2012 г.
- ^ «Неизменяемость в JavaScript: противоположный взгляд» . Десалас работает .
- ^ «API коллекций Scala 2.8 — конкретные неизменяемые классы коллекций» . Scala-lang.org . Проверено 14 апреля 2014 г.
Внешние ссылки [ править ]
- Неизменяемые объекты в C# за 3 простых шага.
- Статья Теория и практика Java: Мутировать или не мутировать? из Брайан Гетц – IBM DeveloperWorks копия сохранена в Интернет-архиве Брайан Гетц из IBM DeveloperWorks – сохраненная копия в Интернет-архиве
- Неизменяемые объекты с JavaPractices.com
- Неизменяемые объекты из репозитория шаблонов Портленда
- Immutable.js от Facebook
- Неизменяемые структуры в C#. Архивировано 21 декабря 2017 г. в проекте с открытым исходным кодом Wayback Machine в Codeplex.
- Неизменяемые коллекции в официальной библиотеке .NET от Microsoft
- Неизменяемые объекты в C# от Tutlane.com