c#中的不可变和只读是什么意思?
本文关键字:是什么 意思 只读 不可变 | 更新日期: 2023-09-27 18:03:37
是正确的,它是不可能改变一个不可变对象的值?
我想了解关于readonly
的两个场景:
-
如果我有一个集合并将其标记为
readonly
,如下所示。我还能打给_items.Add
吗?private readonly ICollection<MyItem> _items;
-
对于以下变量,如果稍后我调用
_metadata.Change
,这将改变Metadata
实例中一对成员变量的内部值。_metadata
仍然是不可变的吗?private readonly Metadata _metadata;
对于上面的两个变量,我完全理解我不能在初始化器和构造函数之外直接给它们赋新值。
我建议你阅读Eric Lippert的系列博客文章。第一部分是c#中的不变性,第一部分:不变性的种类。非常有用的信息和帮助,一如既往。本系列详细描述了只读、不可变等变量的含义。
一般来说,readonly
只意味着你不能在构造函数之外重新分配一个字段。字段本身可以被修改,只要它保持相同的实例。所以,是的,您可以将元素添加到存储在readonly
字段中的集合中。
关于可变性,这是更复杂的,它取决于你考虑什么样的可变性。当Metadata
内部值是引用并且这些引用本身(它指向的实例)没有改变时,您可以说Metadata
保持不变。但是它在逻辑上是变异的。
将字段标记为只读仅意味着不能更改该字段的值。它与物体的内部状态无关。在您的示例中,虽然您不能将新的元数据对象分配给_metadata字段,也不能将新的iccollection分配给_items字段(即在构造函数之外),但您可以更改存储在这些字段中的现有对象的内部值。
不可变对象是一旦创建就不能更改的对象。在c#中字符串是不可变的。如果你看一下字符串操作例程,你会发现它们都返回一个新的、修改过的字符串,而原始字符串保持不变。
这大大简化了字符串处理。当你有一个对字符串的引用时,你可以确保没有其他人会在你的脚下意外地改变它。
readonly
是另一回事。这意味着引用一旦设置就不能更改,并且只能在对象构造期间设置。在您的示例中,您可以更改_items
的内容或_metadata
的属性,但您不能将另一个ICollection<MyItem>
分配给_items
成员或将另一个Metadata
实例分配给_metadata
。
readonly
在对对象的引用上设置。不变性是对象本身的属性。这些可以自由组合。为了确保属性不会以任何方式被改变,它应该是对不可变对象的只读引用。
没有什么可以阻止您更改存储在readonly
字段中的对象。所以你可以在构造函数/初始化器之外调用_items.Add()
和metadata._Change()
。readonly
只阻止在构造后给赋新值。
private readonly ICollection<MyItem> _items;
不阻止添加项。这只是防止_items
被重新分配。_metadata
也是如此。
_metadata
的可访问成员可以被修改,_metadata
不能被重新分配。- 是的。 只读不等于不可变。你仍然可以调用_metadata.Change.
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();
}
}