什么是可变类。我们如何在C#中创建一个可变且不可变的类

奔跑的兔子

在CMMI 5级公司访谈中,有人问我如何在C#中创建可变且不变的类。我听说过可变和不可变的意思是可以更改不能更改,例如StringStringBuilder

但是,我不知道如何创建一个可变的类和不可变的类。我们为此提供了String和String构建器。但是当涉及到创建一个对象时,我被迫在网上搜索该对象,但是找不到有用的东西,因此想到了这里。

但是,我尝试通过在类及其Getter上定义一个属性来创建它,我创建了一个字符串新对象来复制它。但未能成功理解。

另外,我提到在stackoverflow中已经问过一个关于不可变和可变的问题。但是,我的问题是不同的。我想知道是否要创建一个可变的类,那么除了使用String或其他可变类之外,我将如何处理它。

更新:

截至2020年末,用于.NET 5.0的C#9.0已发布,支持不可变的记录类型(支持复制构造函数,并且with操作员可以轻松创建具有新属性值的新实例)。

原始答案,写在C#9.0(和8.0)发布之前:

C#对constC ++正确性的支持程度不如C ++(忽略代码契约),但它仍提供有助于使用的readonly修饰符(以及C#6.0中的真正只读自动属性)。

C#还缺乏对Record类型的语法支持,不幸的是,C#是从C#7中提取的,因此我们不得不再等一年(更新:截至2018年中,C#8.0有望具有Record类型,但C#8.0可能鉴于其非常长的新功能列表,该功能要到2020年才能最终发布)。

无论如何,.NET中的不可变类型只是POCO 1,无法在构造后修改其状态。请注意,只有在您的类型将每个字段都标记为readonly且每个复杂(即非标量)成员也受到类似约束的情况下,编译器才会强制执行此操作

如果您的类型具有任何数组成员,则该类型不能真正不可变,因为C#中没有强制执行只读缓冲区(C ++确实如此)。这意味着在实践中,C#中的“不可变”类型只是精心设计的POCO,消费者(将按规则行事(例如,不作反思))可以对使用它时进行某些假设,例如不可变类型本质上是线程安全的。就是这样。运行时没有特殊的AOT或JIT优化,也没有任何特殊的行为。这是一种非常“人为因素”的东西。

下面的此类是不可变的:

class Immutable {
    private readonly String foo;

    public Immutable(String foo, String bar) {
        this.foo = foo;
        this.Bar = bar;
    }

    public String Bar { get: }

    public String Baz { get { return this.foo.Substring( 0, 2 ); } }
}

它是不可变的,因为每个字段(即其实例状态)都是readonly并且都是不可变的(我们只知道这是因为System.String众所周知,它是不可变的)。如果将foo其更改为StringBuilder或,XmlElement则它将不再是不变的。

请注意,严格来说,readonly修饰符对于不变性不是必需的,它只是使它易于演示,并且确实增加了一定程度的编译时强制执行(可能还对运行时进行了优化)。

为了进行比较,该类不是不可变的(即,它是可变的):

class Mutable {
    private readonly Int32[] values;
    public Mutable(Int32 values) {
        this.values = values;
    }

    public Int32[] GetValues() {
        return this.values;
    }
}

这是可变的,因为:

  1. Int32[] (数组类型)是可变的
  2. 它通过以下方式返回对可变数组的引用 GetValues
  3. 它在构造期间接受可变的对象参数。

这是一个说明为什么它不是不变的示例:

Int32[] values = { 0, 1, 2, 3 };
Mutable mutable = new Mutable( values );

Print( mutable.GetValues() ); // prints "0, 1, 2, 3"

values[0] = 5;

Print( mutable.GetValues() ); // prints "5, 1, 2, 3"

如果Mutable是不可变的,则values使用Mutable的API时,对的后续更改将是不可见的:对的第二次调用Print将显示与第一次相同的输出。

但是,即使使用数组或复杂类型,也可以具有不可变的类型:这是通过隐藏所有修改状态的方法来实现的。例如,使用returnReadOnlyCollection<Int32>代替,Int32[]并始终对传入的所有复杂和可变值执行深层复制/克隆。但是编译器,JIT和运行时仍然不够成熟,无法确定这使对象类型变得不可变-因此,为什么必须记录它并信任使用者可以正确使用它(或者,如果您是使用者,则可以信任上游开发人员他们正确地实施了它)

这是一个包含数组的不可变类型的示例:

class Immutable {
    private readonly Int32[] values;
    public Mutable(Int32 values) {
        if( values == null ) throw new ArgumentNullException(nameof(values)); 
        this.values = (Int32[])values.Clone();
    }

    public IReadOnlyList<Int32> GetValues() {
        return this.values;
    }
}
  • 输入数组Array.Clone()在构造期间是浅复制的(使用)-因此,将来传递给构造函数的对象的任何更改都不会影响任何Immutable类实例。
    • 如果values数组包含不可更改的非标量值,则构造函数将必须对元素执行“深度复制”,以确保其值与将来其他位置的更改隔离开来。
  • values阵列永远不会直接暴露给消费者。
  • GetValues()返回内部数组IReadOnlyList<T> 视图(.NET 4.5中的新增功能)。这比返回ReadOnlyCollection<T>包装器(.NET 2.0中引入)更轻巧

1:POCO是“普通的旧CLR对象”类型,在实践中表示有任何classstruct没有要求它继承某些父超类型或实现任何特定接口。该术语通常用于引用诸如Linq-to-SQL,Entity Framework或NHibernate之类的ORM库,这些库(在其早期版本中)要求每个实体类都派生自某种基本实体类型,或采用某些技术(例如INotifyPropertyChanged)。有关更多详细信息,请参见此处:Entity Framework中的POCO是什么?

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

我如何在C ++中声明一个不可变的类(Java示例)

来自分类Dev

如果包装器类已经不可变,为什么我们需要Atomic *类?

来自分类Dev

我需要一个特定的示例,说明如何在不可变的_case_类的主构造函数中定义局部参数

来自分类Dev

如何在JS中创建对象的可变且不可变的副本

来自分类Dev

在不可变的类中创建字段“ Final”?

来自分类Dev

在不可变的类中创建字段“ Final”?

来自分类Dev

我可以将此C#类称为“不可变的”吗?

来自分类Dev

C#形式和不可变类

来自分类Dev

C#中的通用不可变类

来自分类Dev

如何在双向关联中实例化不可变的类?

来自分类Dev

如何使类在python中不可变?

来自分类Dev

Java中的不可变类

来自分类Dev

Java中的不可变类

来自分类Dev

如何在Scala中创建功能齐全的不可变类?

来自分类Dev

如何在以用户定义的类对象作为类成员的Java中创建用户定义的不可变类?

来自分类Dev

我们如何确保我们只能创建一个类的实例?

来自分类Dev

我们如何确保我们只能创建一个类的实例?

来自分类Dev

使类不可变

来自分类Dev

我们如何使用可变引用来维护类的不变性

来自分类Dev

在C ++中,我们可以为每个整数创建一个类吗?

来自分类Dev

为类创建一个包装器以避免字节中的可变参数

来自分类Dev

Scala:为什么可变Map和不可变Map在作为键的同一个自定义类实例上有不同的结果?

来自分类Dev

创建不可变类的最佳设计方法

来自分类Dev

如何创建不可变类的默认构造函数

来自分类Dev

为什么LinkedListNode类是不可变的?

来自分类Dev

我如何创建一个接受可变数量的int的C ++构造函数

来自分类Dev

我如何创建一个接受可变数量的int的C ++构造函数

来自分类Dev

为什么forEach中的迭代器对于结构是不可变的,但对于类却是可变的?

来自分类Dev

如何使下面的java类不可变

Related 相关文章

  1. 1

    我如何在C ++中声明一个不可变的类(Java示例)

  2. 2

    如果包装器类已经不可变,为什么我们需要Atomic *类?

  3. 3

    我需要一个特定的示例,说明如何在不可变的_case_类的主构造函数中定义局部参数

  4. 4

    如何在JS中创建对象的可变且不可变的副本

  5. 5

    在不可变的类中创建字段“ Final”?

  6. 6

    在不可变的类中创建字段“ Final”?

  7. 7

    我可以将此C#类称为“不可变的”吗?

  8. 8

    C#形式和不可变类

  9. 9

    C#中的通用不可变类

  10. 10

    如何在双向关联中实例化不可变的类?

  11. 11

    如何使类在python中不可变?

  12. 12

    Java中的不可变类

  13. 13

    Java中的不可变类

  14. 14

    如何在Scala中创建功能齐全的不可变类?

  15. 15

    如何在以用户定义的类对象作为类成员的Java中创建用户定义的不可变类?

  16. 16

    我们如何确保我们只能创建一个类的实例?

  17. 17

    我们如何确保我们只能创建一个类的实例?

  18. 18

    使类不可变

  19. 19

    我们如何使用可变引用来维护类的不变性

  20. 20

    在C ++中,我们可以为每个整数创建一个类吗?

  21. 21

    为类创建一个包装器以避免字节中的可变参数

  22. 22

    Scala:为什么可变Map和不可变Map在作为键的同一个自定义类实例上有不同的结果?

  23. 23

    创建不可变类的最佳设计方法

  24. 24

    如何创建不可变类的默认构造函数

  25. 25

    为什么LinkedListNode类是不可变的?

  26. 26

    我如何创建一个接受可变数量的int的C ++构造函数

  27. 27

    我如何创建一个接受可变数量的int的C ++构造函数

  28. 28

    为什么forEach中的迭代器对于结构是不可变的,但对于类却是可变的?

  29. 29

    如何使下面的java类不可变

热门标签

归档