【问题标题】:Immutable type and property in C#C# 中的不可变类型和属性
【发布时间】:2011-02-02 00:24:30
【问题描述】:

C#中的不可变类型和不可变属性是什么意思?能举个简单的例子吗?

【问题讨论】:

    标签: c# types immutability


    【解决方案1】:

    不可变类型是一种只能在初始化时设置其属性的类型。一旦创建了对象,就无法再更改任何内容。不可变属性只是一个只读属性。

    在以下示例中,ImmutableType 是具有一个属性 Test 的不可变类型。测试是一个只读属性。只能在施工时设置。

    class ImmutableType
    {
        private readonly string _test;
        public string Test
        {
            get { return _test; }
        }
    
        public ImmutableType(string test)
        {
            _test = test;
        }
    }
    

    另请参阅The Wikipedia articlesome Stack Overflow questions 关于该主题。

    【讨论】:

    • 您可以通过在 _test 前面加上 readonly 来强制对未来的类更改保持不变性。唉,遗憾的是 C# 中没有不可变的自动实现属性。
    • 当您的属性是引用类型时,这会有点棘手。那么,不可变的定义可以解释为 reference 是不可变的 object graph 是不可变的(取决于您的需要,但通常是后者 - 请参阅 Wiki 文章)。
    • 对于引用类型,这意味着您只能实例化它并分配给它一次。例如,我可以有一个“只读”列表,但我可以在该列表中添加和删除我想要的所有内容。
    • 注意:Sam 的评论不再正确。从 C# 6 开始,您可以使用 { get; } 来拥有不可变的自动实现属性
    • 可以在字符串Test前面放公众吗?我想是的。
    【解决方案2】:

    除了上面@fretje 的回答,在C#6 和更高版本中,getter-only auto properties 现在已经实现,它允许不可变的自动属性,而不需要额外的显式private readonly 支持字段。等效代码将缩写为:

    class ImmutableType
    {
        public string Test
        {
            get; // No Set at all, not even a private set.
        }
    
        public ImmutableType(string test)
        {
            Test = test; // The compiler understands this and initializes the backing field
        }
    }
    

    请注意,private set 仅提供对同一类中属性更改的受限封装,因此并不是真正的不可变:

    public string Test
    {
        get;
        private set;  // Not immutable, since this is still mutable from within the class
    }
    

    关于不变性的更多信息

    正如其他人所说,immutable Property 是一个一旦设置就无法更改的属性。 'only' 值的设置是在构造过程中完成的。

    immutable Type 是一种类型,其中所有(外部可见的)属性和字段都是不可变的 - 例如,最初为 C#7 (hopefully now 8) 计划的“记录”类型将是不可变类型。不可变类型的其他示例是Tuples,以及所有anonymous classes

    不可变字段应使用 C# 中的 readonly 关键字进行限定 - 这是编译器强制执行的,以确保没有其他代码尝试在构造函数之外更改字段。

    在可能的情况下,字段、变量和属性的不变性被认为是一种好的做法,因为这大大减少了错误的表面积(因为字段表示对象的状态,防止更改字段会减少状态的数量)。

    不变性的好处在多线程程序中尤其重要,在多线程程序中,两个或多个线程同时访问同一个对象。由于多个并发读取线程可以安全地读取字段或属性的值,因此程序员无需担心其他线程更改字段相关的线程安全问题(因为禁止更改属性)

    在处理由多个组合对象组成的复杂对象时,不变性的一个常见缺点是需要“一次性”构建整个图,这可能会导致代码混乱。这里一个常见的解决方案是使用Builder pattern作为脚手架,它允许在步骤中构建一个瞬态的、可变的表示,然后在最后的.Build() 步骤中获得最终的、不可变的对象。

    【讨论】:

      【解决方案3】:

      fretje 是正确的。最流行的不可变类型示例是 C# 中的 string 对象。这就是StringBuilder 存在的全部原因。

      【讨论】:

        【解决方案4】:

        在 C# 中对不变性没有明确的定义:

        • 它通常要求所有公共字段都是只读的,所有公共属性都有 init setter 或没有 setter。

        • 它也可以将其扩展到私有成员(尽管通常使用可变私有成员来缓存值,即哈希码)

        • 这也可能意味着所有成员本身都是不可变类型。否则消费者仍然可以通过调用成员方法来修改对象

        • 这也可能意味着所有方法在使用相同的参数调用时总是返回相同的输出。特别是不可变的 GetHashCode() 意味着该对象可以安全地用作字典等中的键

        一个更强大且通常更有用的概念是 - 'Data' 一种不可变类型,它也具有值语义(通常通过继承 IEquatable<T>

        以下是满足上述所有要求的“数据”类型示例:

        record MyData(int age, DateTime JoinDate);
        

        (有关数据类型如何有用的更多信息,请参阅项目 F

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-12
          • 1970-01-01
          • 2016-12-11
          • 2011-04-28
          • 1970-01-01
          • 2010-11-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多