在C#中,不可变和只读是什么意思?

是否正确无法更改不可变对象的值?

我有两个关于readonly场景,我想了解:

  1. 如果我有一个集合并将其标记为readonly ,如下所示。 我还可以打电话给_items.Add吗?

     private readonly ICollection _items; 
  2. 并且对于以下变量,如果稍后我调用_metadata.Change ,它将更改Metadata实例中的一对成员变量的内部值。 _metadata仍然是不可变的吗?

     private readonly Metadata _metadata; 

对于上面的两个变量,我完全理解我不能在初始化器和构造器之外直接为它们分配新值。

我建议你阅读Eric Lippert撰写的一系列博客文章。 第一部分是C#中的不变性:第一部分: 不变性的种类 。 一如既往地提供非常丰富的信息和帮助。 该系列文章详细描述了变量是只读的不可变的等等。

通常, readonly仅表示您无法在构造函数外重新分配字段。 只要字段本身保持不变,就可以修改字段本身。 所以,是的,您可以将元素添加到存储在readonly字段中的集合中。

关于可变性,这是更复杂的,它取决于你考虑什么样的可变性。 当Metadata内部值是引用并且那些引用本身(它指向的实例)不会改变时,您可以说Metadata不会发生变异。 但它在逻辑上是突变的。 有关更多见解,请参阅Eric的post。

将字段标记为只读意味着您无法更改该字段的值。 它与那里物体的内部状态无关。 在您的示例中,虽然您无法将新的元数据对象分配给_metadata字段,也无法将新的ICollection分配给_items字段(在构造函数之外),您可以更改存储在的现有对象的内部值。那些领域。

不可变对象是一旦创建就无法更改的对象。 在C#中,字符串是不可变的。 如果查看字符串操作例程,您可以看到它们都返回一个新的,已修改的字符串,并保持原始字符串不变。

这有利于字符串处理。 当你有一个字符串的引用时,你可以确定没有其他人会在你脚下意外地改变它。

readonly是另一回事。 这意味着一旦设置就无法更改引用 ,并且只能在对象构造期间设置引用 。 在您的示例中,您可以更改_items的内容或_metadata的属性,但不能将另一个ICollection分配给_items成员或另一个Metadata实例到_metadata

readonly设置在对象的引用上。 不变性是对象本身的属性。 这些可以自由组合。 为了确保不以任何方式更改属性,它应该是对不可变对象的只读引用。

没有什么可以防止您改变存储在readonly字段中的对象。 所以你可以在构造函数/初始化程序之外调用_items.Add()metadata._Change()readonly仅阻止您在构造后为这些字段分配新值。

private readonly ICollection _items;

不会阻止添加项目。 这样可以简单地防止重新分配_items_metadata也是如此。 可以更改_metadata可访问成员 – 无法重新分配_metadata

  1. 是。
  2. Readonly不等于不可变。 您仍然可以调用_metadata.Change。

readonly关键字适用于变量 – 这意味着您不能为其分配另一个值,但您可以更改其内部状态。 这就是为什么你可以改变一个集合的项目,但你不能为变量分配一个新的集合。

在像C ++这样的语言中,关键字“const”有很多不同的用途,开发人员传递常量参数以及常量值的常量指针也很容易。

这在C#中并不容易。 我们需要按定义构建不可变类,这意味着一旦创建了一个实例,就无法以编程方式进行更改。

由于关键字final及其用法,Java比C#更容易一些。

让我们考虑这个例子,看看它是多么不可变..

 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(); } }