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

时间:2011-07-27 18:09:31

标签: c# immutability readonly

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

我想了解readonly的两个方案:

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

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

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

8 个答案:

答案 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)

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

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