简体   繁体   English

不可变结构比可变结构有什么好处?

[英]What are the benefits of an immutable struct over a mutable one?

I already know the benefit of immutability over mutability in being able to reason about code and introducing less bugs, especially in multithreaded code. 我已经知道不可变性的好处在于能够推断代码并引入更少的错误,尤其是在多线程代码中。 In creating structs, though, I cannot see any benefit over creating a completely immutable struct over a mutable one. 但是,在创建结构时,我看不到在可变结构上创建完全不可变结构的任何好处。

Let's have as an example of a struct that keeps some score: 让我们作为一个保持一些分数的结构的例子:

struct ScoreKeeper {
    var score: Int
}

In this structure I can change the value of score on an existing struct variable 在这个结构中,我可以改变现有结构变量的得分值

var scoreKeeper = ScoreKeeper(score: 0)
scoreKeeper.score += 5
println(scoreKeeper.score)
// prints 5

The immutable version would look like this: 不可变版本看起来像这样:

struct ScoreKeeper {
    let score: Int

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

And its usage: 它的用法:

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

What I don't see is the benefit of the second approach over the first, since structs are value types. 我没有看到的是第二种方法优于第一种方法的好处,因为结构是值类型。 If I pass a struct around, it always gets copied. 如果我传递一个结构,它总是被复制。 So it does not seem to matter to me if the structure has a mutable property, since other parts of the code would be working on a separate copy anyway, thus removing the problems of mutability. 因此,如果结构具有可变属性,对我来说似乎并不重要,因为代码的其他部分无论如何都将在单独的副本上工作,从而消除了可变性的问题。

I have seen some people using the second example, though, which requires more code for no apparent benefit. 我看到有些人使用第二个例子,这需要更多的代码,没有明显的好处。 Is there some benefit I'm not seeing? 我有没有看到一些好处?

Your remark about copying value types is very good. 关于复制值类型的说法非常好。 Maybe this doesn't make much sense in particular language (swift) and particular compiler implementation (current version) but in general if the compiler knows for sure that the data structure is immutable, it could eg use reference instead of a copy behind the scenes to gain some performance improvement. 也许这在特定语言(swift)和特定编译器实现(当前版本)中没有多大意义,但一般来说,如果编译器确定数据结构是不可变的,它可以例如在幕后使用引用而不是副本获得一些性能提升。 This could not be done with mutable type for obvious reasons. 出于显而易见的原因,这不能用可变类型完成。

Even more generally speaking, limitation means information. 更一般地说,限制意味着信息。 If you limit your data structure somehow, you gain some extra knowledge about it. 如果以某种方式限制数据结构,则可以获得一些额外的知识。 And extra knowledge means extra possibilities ;) Maybe the current compiler does not take advantage of them but this does not mean they are not here :) 额外的知识意味着额外的可能性;)也许当前的编译器没有利用它们,但这并不意味着它们不在这里:)

Good analysis, especially pointing out that structs are passed by value and therefore will not be altered by other processes. 良好的分析,特别指出结构是按价值传递的,因此不会被其他过程改变。

The only benefit I can see is a stylistic one by making the immutability of the element explicit. 我能看到的唯一好处是通过使元素的不变性明确而具有风格。

It is more of a style to make value based types be treated on par with object based types in object oriented styles. 在面向对象的样式中,使基于值的类型与基于对象的类型相同,这更像是一种风格。 It is more of a personal choice, and I don't see any big benefits in either of them. 这更像是个人选择,我认为其中任何一个都没有任何好处。

In general terms, immutable objects are less costly to the system than mutable ones. 一般而言,不可变对象对系统的成本低于可变对象。 Mutable objects need to have infrastructure for taking on new values, and the system has to allow for the fact that their values can change at any time. 可变对象需要具有用于接受新值的基础结构,并且系统必须允许其值可以随时改变。

Mutable objects are also a challenge in concurrent code because you have to guard against the value changing out from under you from another thread. 可变对象在并发代码中也是一个挑战,因为你必须防止从另一个线程中更改你的值。

However, if you are constantly creating and destroying unique immutable objects, the overhead of creating new ones becomes costly quite quickly. 但是,如果您不断创建和销毁唯一不可变对象,则创建新对象的开销会很快变得昂贵。

In the foundation classes, NSNumber is an immutable object. 在基础类中,NSNumber是一个不可变对象。 The system maintains a pool of NSNumber objects that you've used before, and under the covers, gives you back an existing number if you ask for one with the same value as one you created before. 系统维护一个您以前使用过的NSNumber对象池,如果您要求的值与您之前创建的值相同,则会为您提供现有数字。

That's about the only situation in which I could see value in using static structs - where they don't change very much and you have a fairly small pool of possible values. 这是我唯一能够看到使用静态结构的价值的情况 - 它们不会发生很大的变化而且你有一个相当小的可能值池。 In that case you'd probably want to se up your class with a "factory method" that kept recently used structs around and reused them if you asked for a struct with the same value again. 在这种情况下,您可能希望使用“工厂方法”来保持您的类,该方法保留最近使用的结构并重新使用它们,如果您再次请求具有相同值的结构。

Such a scheme could simplify concurrent code, as mentioned above. 如上所述,这种方案可以简化并发代码。 In that case you wouldn't have to guard against the values of your structs changing in another thread. 在这种情况下,您不必在另一个线程中防止更改结构的值。 If you were using such a struct, you could know that it would never change. 如果您使用这样的结构,您可能知道它永远不会改变。

Different approaches will facilitate different kinds of changes to the code. 不同的方法将促进对代码的不同类型的更改。 An immutable structure is very similar to an immutable class object, but a mutable structure and a mutable class object are very different. 不可变结构与不可变类对象非常相似,但是可变结构和可变类对象是非常不同的。 Thus, code which uses an immutable structure can often be readily adapted if for some reason it becomes necessary to use a class object instead. 因此,如果由于某种原因需要使用类对象,则通常可以容易地调整使用不可变结构的代码。

On the flip side, use of an immutable object will often make the code to replace a variable with a modified version more brittle in case additional properties are added to the type in question. 另一方面,使用不可变对象通常会使代码替换具有修改版本的变量更加脆弱,以防附加属性添加到相关类型中。 For example, if a PhoneNumber type includes methods for AreaCode, LocalExchange, and LocalNumber and a constructor that takes those parameters, and then adds an "optional" fourth property for Extension, then code which is supposed to change the area codes of certain phone numbers by passing the new area code, LocalExchange, and LocalNumber, to the three-argument constructor will erase the Extension property of every phone number, while code which could write to AreaCode directly wouldn't have had that problem. 例如,如果PhoneNumber类型包括AreaCode,LocalExchange和LocalNumber的方法以及获取这些参数的构造函数,然后为Extension添加“可选”第四个属性,那么应该更改某些电话号码的区号的代码通过将新区号,LocalExchange和LocalNumber传递给三参数构造函数将擦除每个电话号码的Extension属性,而可直接写入AreaCode的代码将不会出现该问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM