不可变结构体相比可变结构体有哪些优点?

4
我已经知道在编写代码和减少错误方面,不可变性比可变性更有优势,特别是在多线程代码中。但是,在创建结构体时,我无法看到创建完全不可变的结构体相比创建可变结构体的任何优势。
让我们以一个保持一些分数的结构体作为例子:
struct ScoreKeeper {
    var score: Int
}

在这个结构中,我可以更改现有结构变量上的分数值。
var scoreKeeper = ScoreKeeper(score: 0)
scoreKeeper.score += 5
println(scoreKeeper.score)
// prints 5

不可变版本应该是这样的:
struct ScoreKeeper {
    let score: Int

    func incrementScoreBy(points: Int) -> ScoreKeeper {
        return ScoreKeeper(score: self.score + points)
    }
}

它的用法:

let scoreKeeper = ScoreKeeper(score: 0)
let newScoreKeeper = scoreKeeper.incrementScoreBy(5)
println(newScoreKeeper.score)
// prints 5

我不明白第二种方法比第一种方法的好处在哪里,因为结构体是值类型。如果我传递一个结构体,它总是会被复制。所以对我来说似乎没有关系这个结构体是否有一个可变属性,因为代码的其他部分会在另一个副本上工作,从而消除了可变性的问题。
虽然我看到一些人使用第二个例子,但它需要更多的代码,而没有明显的好处。我是否有所忽略?

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user395760
2
这个主题是关于C#的,但我认为其中一些论点也适用于Swift。 - Martin R
5个回答

2
不同的方法会促进对代码进行不同类型的更改。一个不可变的结构非常类似于一个不可变的类对象,但一个可变的结构和一个可变的类对象却非常不同。因此,如果出现必须使用类对象的情况,使用不可变结构的代码通常可以很容易地适应。
另一方面,使用不可变对象通常会使替换变量为修改版本的代码更加脆弱,以防需要添加类型的其他属性。例如,如果 PhoneNumber 类型包括 AreaCode、LocalExchange 和 LocalNumber 方法以及接受这些参数的构造函数,然后添加了一个“可选”的第四个 Extension 属性,则通过将新的区号、LocalExchange 和 LocalNumber 传递给三个参数构造函数来更改某些电话号码的区号的代码将擦除每个电话号码的 Extension 属性,而可以直接写入 AreaCode 的代码则没有这个问题。

2

你对于复制值类型的评论非常好。也许在某种具体的语言(如Swift)和特定的编译器实现(当前版本)中,这可能没有太多意义,但一般来说,如果编译器确定数据结构是不可变的,那么它可以在幕后使用引用而不是副本,从而获得一些性能提升。这对于可变类型来说是不可能做到的,原因显而易见。

更普遍地说,限制意味着信息。如果你以某种方式限制了你的数据结构,你就会获得一些关于它的额外知识。而额外的知识意味着额外的可能性 ;) 也许当前的编译器没有利用它们,但这并不意味着它们不存在 :)


1
很好的分析,特别指出结构体是按值传递的,因此不会被其他进程改变。唯一的好处我认为是通过明确元素的不可变性来实现风格上的统一。

1

在面向对象的风格中,更多的是将基于值的类型与基于对象的类型平等对待。这更多是个人选择,我并没有看到其中有任何重大好处。


1
一般来说,不可变对象比可变对象对系统的成本更低。可变对象需要基础架构来接受新值,并且系统必须允许它们的值随时更改。
可变对象在并发代码中也是一个挑战,因为你必须防止另一个线程从下面改变值。
但是,如果您不断地创建和销毁唯一的不可变对象,则创建新对象的开销很快就会变得昂贵。
在基础类中,NSNumber是一个不可变对象。系统维护了一个NSNumber对象池,您之前使用过的对象,在幕后,如果您请求与之前创建的对象具有相同值的对象,则会将其返回给您。
这大概是我能看到使用静态结构的唯一情况 - 其中它们不太改变,而且可能的值范围相对较小。在这种情况下,您可能希望使用“工厂方法”设置类,以保留最近使用的结构并在再次请求具有相同值的结构时重用它们。
这样的方案可以简化并发代码,如上所述。在这种情况下,您不必防止您的结构的值在另一个线程中发生变化。如果您正在使用这样的结构,则可以知道它永远不会改变。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接