不能更改不可变对象的值是否正确?
我想了解关于
readonly
的两个场景:
如果我有一个收藏并标记为
readonly
会怎么样,像下面这样。我还能打电话给_items.Add
吗?
private readonly ICollection<MyItem> _items;
还有下面的变量,如果稍后我调用
_metadata.Change
这将更改 Metadata
实例中一对成员变量的内部值。 _metadata
仍然是不可变的吗?
private readonly Metadata _metadata;
对于上面的两个变量,我完全理解我不能在初始化器和构造函数之外直接为它们分配新值。
我建议您阅读 Eric Lippert 的系列博文。第一部分是C# 中的不变性第一部分:不变性的种类。一如既往地非常有用和有用。该系列详细描述了变量是 readonly,immutable 等的含义。
一般来说,
readonly
只表示不能在构造函数外重新赋值一个字段。只要保持相同的实例,就可以修改字段本身。所以是的,您可以将元素添加到存储在 readonly
字段中的集合中。
关于可变性,这更复杂,它有点取决于你考虑什么样的可变性。当
Metadata
内部值是引用并且这些引用本身(它指向的实例)没有改变时,您可以说 Metadata
保持不变。但是它在逻辑上发生了变异。有关更多见解,请参阅 Eric 的帖子。
将字段标记为只读意味着您不能更改该字段的值。它与那里的对象的内部状态无关。在您的示例中,虽然您无法将新的元数据对象分配给 _metadata 字段,也无法将新的 ICollection 分配给 _items 字段(在构造函数之外),但您可以更改存储在中的现有对象的内部值那些领域。
一个 immutable 对象是一个一旦创建就不能改变的对象。在 C# 中,字符串是不可变的。如果您查看字符串操作例程,您会发现所有这些例程都返回一个新的、修改后的字符串并保持原始字符串不变。
这大大促进了字符串处理。当你有一个字符串的引用时,你可以确定没有其他人会在你脚下意外地改变它。
readonly
是另一回事。这意味着 reference 不能更改,一旦设置并且它只能在对象构造期间设置。在您的示例中,您可以更改_items
的内容或_metadata
的属性,但不能将另一个ICollection<MyItem>
分配给_items
成员或另一个Metadata
实例到_metadata
。
readonly
设置在对象的reference 上。不变性是对象本身的属性。这些可以自由组合。为确保属性不以任何方式更改,它应该是对不可变对象的只读引用。
没有什么可以阻止您改变存储在
readonly
字段中的对象。所以你可以在构造函数/初始化器之外调用 _items.Add()
和 metadata._Change()
。 readonly
只会阻止您在构建后assigning 新值给这些字段。
private readonly ICollection<MyItem> _items;
不阻止添加项目。这只是防止
_items
被重新分配。 _metadata
也是如此。 _metadata
的可访问成员可以更改 - _metadata
不能重新分配。
readonly 关键字适用于一个变量——这意味着你不能给它赋另一个值,但你可以改变它的内部状态。这就是为什么您可以更改集合的项目但不能将新集合分配给变量的原因。
在像 C++ 这样的语言中,关键字“const”有很多不同的用法,开发人员很容易传递常量参数和常量值的常量指针。
这在 C# 中并不容易。 我们需要根据定义构建不可变类,这意味着一旦创建了一个实例,就无法通过编程方式更改它。
Java 有一个比 C# 更简单的方法,因为关键字 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();
}
}