为什么 C# 数字类型是不可变的?
为什么 int 和 double 是不可变的?每次想要更改值时返回一个新对象的目的是什么?
我问的原因是因为我正在创建一个类:BoundedInt
,它有一个值以及上限和下限。所以我想知道:我也应该使这种类型不可变吗? (或者它应该是一个struct
?)
Why are int
s and double
s immutable? What is the purpose of returning a new object each time you want to change the value?
The reason I ask is because I'm making a class: BoundedInt
, which has a value and an upper and lower bound. So I was wondering: should I make this type immutable too? (Or should it be a struct
?)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
首先:
我认为您可能对值类型的工作方式有误解。这并不像您想象的那样是一项昂贵的操作;它是一种成本高昂的操作。它只是数据的覆盖(与例如新内存的动态分配相反)。
其次:这是一个非常简单的例子来说明为什么数字是不可变的:
当然,这是一个人为的例子。因此,让我们考虑一些更复杂的想法。
可变引用类型
首先,有这样一个:如果 Integer 是可变引用类型怎么办?
然后我们可以有这样的代码:
而且:
这似乎违反直觉:t2.Integer = t1.Integer 行将简单地复制一个值(实际上,它确实如此;但“值”是实际上是一个引用),因此
t2.Integer
将保持独立于t1.Integer
。可变值类型
当然,这可以通过另一种方式来实现,将 Integer 保留为值类型,但保持其可变性:
但是现在假设我们这样做:
基本上,值的不变性> 是高度直观的东西。相反的情况是非常不直观的。
不过,平心而论,我想这是主观的;)
Firstly:
I think you might be mistaken about how value types work. This isn't some costly operation like you may be imagining; it's simply the overwriting of data (as opposed to, e.g., dynamic allocation of new memory).
Secondly: here's a very simple example of why numbers are immutable:
Granted, that is a contrived example. So let's consider a couple more involved ideas.
Mutable reference type
First, there's this one: what if
Integer
were a mutable reference type?Then we could have code like this:
And:
This seems to defy intuition: that the line
t2.Integer = t1.Integer
would simply copy a value (actually, it does; but that "value" is in fact a reference) and thus thatt2.Integer
would remain independent oft1.Integer
.Mutable value type
This could be approached another way, of course, keeping
Integer
as a value type but maintaining its mutability:But now let's say we do this:
Basically, immutability of values is something that is highly intuitive. The opposite is highly unintuitive.
I guess that is subjective, though, in all fairness ;)
整数变量是可变的。然而,整数文字是常量,因此是不可变的。
使用
readonly
可以使整数字段不可变。同样,整数属性可以是仅获取的。Integer variables are mutable. However, integer literals are constants, hence immutable.
It's possible to make an integer field immutable, using
readonly
. Likewise, an integer property could be get-only.作为可变对象,您必须在更改
int
变量之前锁定它(在从单独线程写入 int 的任何多线程代码中)。为什么?假设您正在递增一个
int
,如下所示:在幕后,这是一个 32 位数字。理论上,在32位计算机上你可以加1,并且这个操作可能是原子的;也就是说,它可以一步完成,因为它是在 CPU 寄存器中完成的。不幸的是,事实并非如此;还有更多的事情发生。
如果另一个线程在这个数字正在递增的过程中改变了它怎么办?你的号码会被损坏。
但是,如果您在递增对象之前创建对象的线程安全副本,对线程安全副本进行操作,并在增量完成时返回一个新对象,则可以保证增量是线程安全的;它不会受到其他线程上对原始对象进行的任何操作的影响,因为您不再使用原始对象。实际上,您已经使对象变得不可变。
这是函数式编程背后的基本原理;通过使对象不可变并从函数返回新对象,您可以免费获得线程安全性。
As a mutable object, you have to lock an
int
variable before you change it (in any multi-threaded code that writes to your int from separate threads).Why? Let's say you were incrementing an
int
, like this:Under the hood, this is a 32-bit number. Theoretically, on a 32 bit computer you could add 1 to it, and this operation might be atomic; that is, it would be accomplished in one step, because it would be accomplished in a CPU register. Unfortunately, it's not; there is more going on than this.
What if another thread mutated this number while it was in the middle of being incremented? Your number would get corrupted.
However, if you make a thread-safe copy of your object before you increment it, operate on your thread-safe copy, and return a new object when your increment is complete, you guarantee that your increment is thread safe; it cannot be affected by any operations on the original object that take place on other threads, because you're no longer working with the original object. In effect, you have made your object immutable.
This is the basic principle behind functional programming; by making objects immutable, and returning new objects from functions, you get thread safety for free.
将 BoundedInt 作为可变类型是有意义的,因为它表示在任何时间点都具有特定值的变量,并且该值可以更改,但只能在一定范围内更改。
然而,整数本身不是变量,因此它们不应该是可变的。
It makes sense to have
BoundedInt
as a mutable type because it represents a variable that at any point in time has a specific value and that value can be changed but only within a certain range.However integers themselves aren't variables so they should not be mutable.
任何具有值语义的内容在 C# 中都应该是不可变的。
可变类不能具有值语义,因为您无法覆盖赋值运算符。
可变结构很丑陋,因为您可以轻松地对临时变量调用可变方法。特别是属性返回临时变量。
这就是为什么我不喜欢大多数 Vector 库在其 Vector 结构中使用像 Normalize 这样的变异方法。
Anything with value semantics should be immutable in C#.
Mutable classes can't have value semantics because you can't override the assignment operator.
Mutable structs are ugly because you can easily call a mutating method on a temporary variable. In particular properties return temporary variables.
That's why I don't like that most Vector libraries use mutating methods like Normalize in their Vector struct.
我正在研究神经网络的学术项目。这些网络使用双精度进行大量计算。我在亚马逊云上的 32 台核心服务器上运行了几天。在分析应用程序时,最重要的性能问题是双精度分配!
拥有一个具有可变类型的专用命名空间是公平的。可以强制执行“不安全”关键字以采取额外的预防措施。
I'm working on an academic project with Neural Networks. These networks do heavy computation with doubles. I run it on amazon cloud for days on 32 core servers. When profiling the application, the top performance problem is allocation of double!!
It would be fair to have a dedicated namespace with mutable types. "unsafe" keywords can be enforced for additional precaution.