无法更改不可变对象的值是否正确?
我想了解readonly
的两个方案:
如果我有一个集合并将其标记为readonly
,如下所示。我还可以拨打_items.Add
吗?
private readonly ICollection<MyItem> _items;
还有以下变量,如果稍后我调用_metadata.Change
,它将更改Metadata
实例中一对成员变量的内部值。 _metadata
仍然是不可变的吗?
private readonly Metadata _metadata;
对于上面的两个变量,我完全理解我不能在初始化器和构造函数之外直接为它们分配新值。
答案 0 :(得分:23)
我建议你阅读Eric Lippert撰写的一系列博客文章。第一部分是Immutability in C# Part One: Kinds of Immutability。一如既往,非常翔实,乐于助人。该系列文章详细描述了变量 readonly , immutable 等的含义。
通常,readonly
仅表示您无法在构造函数外重新分配字段。只要字段本身保持不变,就可以修改字段本身。所以,是的,您可以将元素添加到readonly
字段中存储的集合中。
关于可变性,这是更复杂的,它取决于你考虑什么样的可变性。当Metadata
内部值是引用并且那些引用本身(它指向的实例)不会改变时,您可以说Metadata
保持不变。但它在逻辑上突变。有关更多见解,请参阅Eric的帖子。
答案 1 :(得分:7)
仅将字段标记为只读意味着您无法更改该字段的值。它与那里物体的内部状态无关。在您的示例中,虽然您无法将新元数据对象分配给_metadata字段,也无法将新ICollection分配给_items字段(在构造函数之外),您可以更改存储在其中的现有对象的内部值。那些领域。
答案 2 :(得分:4)
immutable 对象是一旦创建就无法更改的对象。在C#中,字符串是不可变的。如果查看字符串操作例程,您可以看到所有这些返回一个新的,修改过的字符串并保持原始字符串不变。
这有利于字符串处理。当你有一个字符串的引用时,你可以确定没有其他人会在你脚下意外地改变它。
readonly
是另一回事。这意味着引用一旦设置就无法更改,并且只能在对象构造期间设置。在您的示例中,您可以更改_items
的内容或_metadata
的属性,但不能将另一个ICollection<MyItem>
分配给_items
成员或其他Metadata
实例到_metadata
。
readonly
在引用上设置为对象。不变性是对象本身的属性。这些可以自由组合。为了确保不以任何方式更改属性,它应该是对不可变对象的只读引用。
答案 3 :(得分:3)
没有什么能阻止您改变存储在readonly
字段中的对象。因此,您可以在构造函数/初始化程序之外调用_items.Add()
和metadata._Change()
。 readonly
仅阻止您在构建后为这些字段分配新值。
答案 4 :(得分:3)
private readonly ICollection<MyItem> _items;
不会阻止添加项目。这样可以防止重新分配_items
。 _metadata
也是如此。 <{1}}的可访问成员可以更改 - _metadata
无法重新分配。
答案 5 :(得分:2)
答案 6 :(得分:2)
readonly关键字适用于变量 - 这意味着您无法为其指定其他值,但您可以更改其内部状态。这就是为什么你可以改变一个集合的项目,但你不能为变量分配一个新的集合。
答案 7 :(得分:0)
在像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();
}
}