2016-10-03 94 views
0

我在CMMI 5级公司采访中被问到如何在C#中创建一个可变和不可变类。我听说过的可变和不可变这意味着可以改变不能改变,像字符串StringBuilder的什么是可变类。我们如何在C#中创建一个可变且不可变的类

但是,我不知道如何创建一个可变类和不可变类。我们有String和String构建器。但是当创建一个,我被迫在网上搜索这个,但找不到任何有用的,所以想到这里问。

但是,我试图通过在类中定义一个属性并在其Getter上创建它,我创建了一个新的字符串对象来复制它。但没有成功理解。

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

+0

可能重复[有什么区别mutable和不可变?](http://stackoverflow.com/questions/3811016/what-is-the-difference-between-mutable-and-不可变) – walther

+0

请在发布之前搜索答案,以免发生重复问题。 – walther

+0

@walther:请阅读我的问题!我问了如何创建一个可变类。我知道什么是可变的和不可变的。但是当谈到创作时,我不知道它。和你提到的问题是不同的! – Yash

回答

2

C#没有与const相同的支持水平 - C++提供的正确性(忽略代码合同),但它仍然提供readonly修饰符(以及C#6.0中真正的只读自动属性),这有助于。

C#也缺乏对Record类型的语法支持,但不幸的是它们是从C#7中提取的,所以我们不得不再等一年。

无论如何,.NET中的不可变类型只是一个POCO,它在构造后不能修改其状态。请注意,只有编译器或运行时才会执行此操作,前提是您的类型的每个字段都标记为readonly,并且每个复杂(即非标量)成员也都受到类似限制。噢,你不能有任何数组成员,因为在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改为StringBuilderXmlElement那么它将不再是不可变的。

注意,严格来说,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. 一个参照可变数组它接受一个施工期间可变对象参数。

下面是一个例子证明,为什么它不是一成不变的:使用Mutable的API时

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将不可见:以Print第二个电话会显示与第一个相同的输出。

但是,即使使用数组或复杂类型,也可以拥有不可变类型:这是通过隐藏修改状态的所有方法完成的。例如,返回ReadOnlyCollection<Int32>而不是Int32[],并始终对传入的所有复杂值和可变值执行深度复制/克隆。但是,编译器,JIT和运行时仍然不够复杂,无法确定这会呈现对象类型不可变 - 因此,为什么必须记录它并相信消费者才能正确使用它(或者如果您是消费者,请相信您的上游设备他们正确实施它)

+0

谢谢戴。它真的帮助了 – Yash

+0

@Yash自从你发布以来,我的回答已经显着延长了。 – Dai

+0

通过确保它只能在构造函数中设置,使用readonly关键字可以很好地解释它。此外,这将防止意外修改其中一个领域,打破不变性。 – Yash