Что означает неизменяемый и readonly на С#?
Правильно ли, что невозможно изменить значение неизменяемого объекта?
У меня есть два сценария относительно readonly
, которые я хочу понять:
-
Что делать, если у меня есть коллекция и отмечаю ее как readonly
, как показано ниже. Могу ли я позвонить _items.Add
?
private readonly ICollection<MyItem> _items;
-
А также для следующей переменной, если позже я вызову _metadata.Change
, который изменит внутренние значения пары переменных-членов в экземпляре Metadata
. Является ли _metadata
еще неизменным?
private readonly Metadata _metadata;
Для обеих переменных выше я полностью понимаю, что я не могу напрямую присваивать им новые значения вне инициализатора и конструкторов.
Ответы
Ответ 1
Я предлагаю вам прочитать серию сообщений в блогах Эрика Липперта. Первая часть Неизменность в С# Часть первая: Виды неизменности. Очень информативно и полезно, как всегда. В серии описывается, что это означает, что переменная должна быть readonly, immutable и т.д. Подробно.
Как правило, readonly
означает только то, что вы не можете повторно назначить поле вне конструктора. Само поле может быть изменено, если оно остается в одном экземпляре. Итак, вы можете добавить элементы в коллекцию, хранящуюся в поле readonly
.
О изменчивости, это более сложно, и это немного зависит от того, какую изменчивость вы считаете. Когда Metadata
внутренние значения являются ссылками, а сами эти ссылки (экземпляры, на которые он указывает) не изменяются, вы можете сказать, что Metadata
остается не мутированным. Но он мутирован логически. См. Сообщения Eric для получения дополнительной информации.
Ответ 2
Маркировка поля только для чтения означает, что вы не можете изменить значение этого поля. Он не влияет на внутреннее состояние объекта. В ваших примерах, в то время как вы не сможете назначить новый объект метаданных в поле _metadata или новый ICollection в поле _items (вне конструктора), вы можете изменить внутренние значения существующих объектов, хранящихся в эти поля.
Ответ 3
Неизменяемый объект - это тот, который нельзя изменить, после его создания. В С# строки неизменяемы. Если вы посмотрите на строковые манипуляционные процедуры, вы увидите, что все они возвращают новую, измененную строку и оставляют исходную строку неизменной.
Это значительно облегчает обработку строк. Когда у вас есть ссылка на строку, вы можете быть уверены, что никто другой не изменит ее под ногами.
readonly
- это что-то еще. Это означает, что ссылка не может быть изменена, после ее установки и что ее можно установить только при построении объекта. В ваших примерах вы можете изменить содержимое _items
или свойства _metadata
, но вы не можете назначить другому ICollection<MyItem>
члену _items
или другому экземпляру Metadata
на _metadata
.
readonly
устанавливается на ссылку на объект. Неизменность - это свойство самого объекта. Они могут быть свободно объединены. Чтобы убедиться, что свойство никоим образом не изменено, оно должно быть ссылкой только на неизменяемый объект.
Ответ 4
Ничто не мешает вам мутировать объект, хранящийся в поле readonly
. Таким образом, вы можете называть _items.Add()
и metadata._Change()
вне конструктора/инициализатора. readonly
только препятствует вам присваивать новые значения этим полям после построения.
Ответ 5
private readonly ICollection<MyItem> _items;
Не препятствует добавлению элементов. Это просто предотвращает повторное назначение _items
. То же самое верно для _metadata
. Доступные члены _metadata
могут быть изменены - _metadata
не может быть повторно назначено.
Ответ 6
- Да.
- Readonly не является неизменным. Вы все равно можете вызвать _metadata.Change.
Ответ 7
Ключевое слово readonly применяется к переменной - это означает, что вы не можете присвоить ей другое значение, но вы можете изменить его внутреннее состояние. Поэтому вы можете изменять элементы коллекции, но вы не можете назначить новую коллекцию этой переменной.
Ответ 8
В таких языках, как С++, существует довольно много разных применений ключевого слова "const", разработчикам отчасти легко передавать постоянные параметры, а также постоянные указатели постоянных значений.
В С# это непросто.
Нам нужно построить неизменяемый класс по определению, что означает, что после создания экземпляра он не может быть изменен программно.
Java имеет немного более легкий путь, чем С# из-за ключевого слова final и его использования.
Давайте рассмотрим этот пример и посмотрим, насколько он непререкаем.
public sealed class ImmutableFoo
{
private ImmutableFoo()
{
}
public string SomeValue { get; private set; }
//more properties go here
public sealed class Builder
{
private readonly ImmutableFoo _instanceFoo = new ImmutableFoo();
public Builder SetSomeValue(string someValue)
{
_instanceFoo.SomeValue = someValue;
return this;
}
/// Set more properties go here
///
public ImmutableFoo Build()
{
return _instanceFoo;
}
}
}
Вы можете использовать его следующим образом
public class Program
{
public static void Main(string[] args)
{
ImmutableFoo foo = new ImmutableFoo.Builder()
.SetSomeValue("Assil is my name")
.Build();
Console.WriteLine(foo.SomeValue);
Console.WriteLine("Hit enter to terminate");
Console.ReadLine();
}
}