是值类型定义不变? [英] Are value types immutable by definition?
问题描述
我经常读到结构
取值应该是一成不变的 - ?是不是他们定义
你?考虑 INT
为不可变的。
INT I = 0;
I = I + 123;
看起来没问题 - 我们得到了一个新的 INT
并指派回 I
。 ?这个
我++是什么;
好了,我们可以把它作为一种快捷方式。
I = I + 1;
有关什么结构
<$ C $ ?C>点
点p =新的点(1,2);
p.Offset(3,4);
这是否真的发生变异点(1,2)
?我们不应该把它作为与 Point.Offset()
以下返回一个新的点快捷方式?
p = p.Offset(3,4);
这种思想的背景是这样的 - 值类型没有身份证怎么能是可变的?你看它至少两次,以确定它是否改变了。但是,你怎么能这样没有身份?
我不想考虑 REF
参数和拳击。我也知道, P = p.Offset(3,4);
表示不变性比好得多p.Offset(3,4);
一样。但问题仍然存在 - 不值类型一成不变的定义
更新
<? p>我觉得有至少涉及两个概念 - 一个变量或字段和变量的值的可变性的易变性
公共类Foo
{
私人点对点;
私人只读点readOnlyPoint;
公共美孚()
{
this.point =新的点(1,2);
this.readOnlyPoint =新点(1,2);
}
公共无效酒吧()
{
this.point =新的点(1,2);
this.readOnlyPoint =新点(1,2); //不能编译。
this.point.Offset(3,4); //现已(4,6)。
this.readOnlyPoint.Offset(3,4); //依然是(1,2)。
}
}
在这个例子中,我们不得不领域 - 一个可变的1和不可改变的。由于值类型字段包含全价值,存放在一个不变的字段中的值类型必须是不可改变的,太。我还是相当的结果感到惊讶 - 我没有ExSpect语言的只读领域保持不变。
变量(除了常数)是永诺可变的,因此,它们意味着没有限制值类型的可变性。
答案似乎不被认为直白易懂我会重组的问题。
由于以下几点。
公开结构美孚
{
公共无效DoStuff(whatEverArgumentsYouLike)
{
//你喜欢做什么都。
}
//把一切你喜欢的 - 场,常量,方法,属性...
}
你能给富
和使用例子 - 这可能包括 REF
参数和拳击 - 所以它不可能重写
所有出现
foo.DoStuff (whatEverArgumentsYouLike);
与
富= foo.DoStuff(whatEverArgumentsYouLike);
这是对象是不可变如果其状态
不会改变,一旦对象有
创建
块引用>
简短的回答:不,值类型是不会被定义为一成不变的。 两个结构和类可以是可变的或不可变的。所有四种组合都是可能的。如果一个结构或类具有非只读的公共领域,以制定者的公共属性,或设置私有字段的方法,它是可变的,因为你可以在不创建该类型的新实例,改变它的状态。
龙答:首先,永恒的问题只适用于与字段或属性结构或类。最基本的类型(数字,字符串和null)本质上是不可变的,因为没有什么(字段/属性)来改变他们。有5是5是5对5的任何操作仅返回另一个不可变的值。
您可以创建可变结构,如
系统。 Drawing.Point
。无论X
和是
有哪些修改结构的领域制定者:点p =新的点(0,0);
p.X = 5;
//我们修改结构,通过属性setter点¯x
//还是一样Point实例,但其状态已经改变了
//它的属性x现在5
有些人似乎混淆不可变性的事实,值类型是按值(因此他们的名字),而不是按引用传递。
无效的主要()
{
点P1 =新的点(0,0);
SETX(P1,5);
Console.WriteLine(p1.ToString());
}
无效SETX(点P2,int值)
{
p2.X =价值;
}
在这种情况下,
Console.WriteLine()
写道:{X = 0,Y = 0}
。在这里,P1
并没有修改,因为SETX()
修改P2
这是的P1
的副本的。这是因为P1
是值类型的,不是因为它的一成不变的(这是不是)。
为什么的应的值类型是不可变的?原因很多...查看这个问题。大多是因为可变的值类型导致各种不那么明显的错误。在上面的例子中,程序员可能期望
P1
是(5,0)
呼叫后SETX()
。或者想象一下,可以在以后更改值排序。然后按预期排序集合将不再进行排序。这同样适用于字典和哈希值。该美妙埃里克利珀(的博客)写了一个有关全系列不变性以及为什么他认为这是C#的未来。 这里是他的一个例子,让你修改只读变量
更新:
:你的榜样
this.readOnlyPoint.Offset(3,4); //依然是(1,2)。
是完全什么利珀特在他的文章提到有关修改只读变量。
偏移(3,4)
其实修改的点
,但它是一个的复制的中readOnlyPoint
,它从来没有分配到任何东西,所以它的丢失。
和是就是可变的值类型是邪恶的:他们让你的认为的要修改的东西,有时,当你实际修改副本,从而导致意想不到的错误。如果
点
是不可改变的,偏移()
必须返回一个新的点
,你不会已经能够将其分配到readOnlyPoint
。然后你去的哦,对了,它的只读的一个原因。为什么我试图去改变它?好东西的编译器现在拦住了我。的
更新:关于你的改写请求......我想我知道你在说什么。在某种程度上,你可以思考结构的作为是的内部的一成不变的,修改结构是相同,但修改后的副本替换它。它甚至可能是什么CLR的内部确实在内存中,就我所知。 (这是闪存的工作方式。你不能编辑只有几个字节,你需要阅读千字节到内存中的一个整块,修改你想要的少,整个块写回)。但是,即使他们是内部不可变这是一个实现细节,对我们开发商的结构用户(其接口或API,如果你愿意),他们的可以的改变。我们不能忽视这一事实,并认为他们是不可变的。
在你说:你不能有字段或变量的值的引用评论。你正在假设每个结构变量具有不同的副本,使得修改一个拷贝不会影响别人。这是不完全正确的。下面标线不会被更换,如果...
接口的IFoo {DoStuff(); }
结构美孚:的IFoo {/ * ... * /}
的IFoo otherFoo =新的Foo();
的IFoo富= otherFoo;
foo.DoStuff(whatEverArgumentsYouLike); //线#1
富= foo.DoStuff(whatEverArgumentsYouLike); //线#2
线#1和#2不具有相同的结果...为什么?因为
富
和otherFoo
引用的相同装箱实例的富的。无论在富
在1号线改为在otherFoo
反映。 2号线替换富
以新的价值和无助于otherFoo
(假设DoStuff ()
返回一个新的的IFoo
实例,并不会修改富
本身)。美孚foo1 =新的Foo(); //创建一审
富foo2的= foo1; //创建一个副本(第二个实例)
的IFoo foo3 = foo2的; //这里无可复制! foo2的和foo3指同一个实例
修改
foo1
不会影响foo2的
或foo3
。修改foo2的
将在foo3
反映,而不是在foo1
。修改foo3
将在foo2的
反映,但不是在foo1
。
混淆? 。坚持不变的值类型和你消除修改其中的任何冲动
更新:第一个代码示例中的固定错字
I frequently read that
struct
s should be immutable - aren't they by definition?Do you consider
int
to be immutable?int i = 0; i = i + 123;
Seems okay - we get a new
int
and assign it back toi
. What about this?i++;
Okay, we can think of it as a shortcut.
i = i + 1;
What about the
struct
Point
?Point p = new Point(1, 2); p.Offset(3, 4);
Does this really mutate the point
(1, 2)
? Shouldn't we think of it as a shortcut for the following withPoint.Offset()
returning a new point?p = p.Offset(3, 4);
The background of this thought is this - how can a value type with no identity be mutable? You have to look at it at least twice to determine if it changed. But how can you do this without an identity?
I don't want to complicate reasoning about this by considering
ref
parameters and boxing. I am also aware thatp = p.Offset(3, 4);
expresses immutability much better thanp.Offset(3, 4);
does. But the question remains - aren't value types immutable by definition?UPDATE
I think there are at least two concepts involved - the mutability of a variable or field and the mutability of the value of a variable.
public class Foo { private Point point; private readonly Point readOnlyPoint; public Foo() { this.point = new Point(1, 2); this.readOnlyPoint = new Point(1, 2); } public void Bar() { this.point = new Point(1, 2); this.readOnlyPoint = new Point(1, 2); // Does not compile. this.point.Offset(3, 4); // Is now (4, 6). this.readOnlyPoint.Offset(3, 4); // Is still (1, 2). } }
In the example we have to fields - a mutable one and a immutable one. Because a value type field contains the whole value, a value type stored in a immutable field must be immutable, too. I am still quite suprised by the result - I did not exspect the readonly field to remain unmodified.
Variables (besides constants) are allways mutable, hence they imply no restriction on the mutability of value types.
The answer seems not to be that straight forward so I will rephrase the question.
Given the following.
public struct Foo { public void DoStuff(whatEverArgumentsYouLike) { // Do what ever you like to do. } // Put in everything you like - fields, constants, methods, properties ... }
Can you give a completed version of
Foo
and an usage example - that may includeref
parameters and boxing - so that it is not possible to rewrite all occurences offoo.DoStuff(whatEverArgumentsYouLike);
with
foo = foo.DoStuff(whatEverArgumentsYouLike);
解决方案An object is immutable if its state doesn’t change once the object has been created.
Short answer: No, value types are not immutable by definition. Both structs and classes can be either mutable or immutable. All four combinations are possible. If a struct or class has non-readonly public fields, public properties with setters, or methods which set private fields, it is mutable because you can change its state without creating a new instance of that type.
Long answer: First of all, the question of immutability only applies to structs or classes with fields or properties. The most basic types (numbers, strings, and null) are inherently immutable because there is nothing (field/property) to change about them. A 5 is a 5 is a 5. Any operation on the 5 only returns another immutable value.
You can create mutable structs such as
System.Drawing.Point
. BothX
andY
have setters which modify the struct's fields:Point p = new Point(0, 0); p.X = 5; // we modify the struct through property setter X // still the same Point instance, but its state has changed // it's property X is now 5
Some people seem to confuse immutablity with the fact that value types are passed by value (hence their name) and not by reference.
void Main() { Point p1 = new Point(0, 0); SetX(p1, 5); Console.WriteLine(p1.ToString()); } void SetX(Point p2, int value) { p2.X = value; }
In this case
Console.WriteLine()
writes "{X=0,Y=0}
". Herep1
was not modified becauseSetX()
modifiedp2
which is a copy ofp1
. This happens becausep1
is a value type, not because it is immutable (it isn't).Why should value types be immutable? Lots of reasons... See this question. Mostly it's because mutable value types lead to all sorts of not-so-obvious bugs. In the above example the programmer might have expected
p1
to be(5, 0)
after callingSetX()
. Or imagine sorting by a value which can later change. Then your sorted collection will no longer be sorted as expected. The same goes for dictionaries and hashes. The Fabulous Eric Lippert (blog) has written a whole series about immutability and why he believes it's the future of C#. Here's one of his examples that lets you "modify" a read-only variable.
UPDATE: your example with:
this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).
is exactly the what Lippert referred to in his post about modifying read-only variables.
Offset(3,4)
actually modified aPoint
, but it was a copy ofreadOnlyPoint
, and it was never assigned to anything, so it's lost.And that is why mutable value types are evil: They let you think you are modifying something, when sometimes you are actually modifying a copy, which leads to unexpected bugs. If
Point
was immutable,Offset()
would have to return a newPoint
, and you would not have been able to assign it toreadOnlyPoint
. And then you go "Oh right, it's read-only for a reason. Why was I trying to change it? Good thing the compiler stopped me now."
UPDATE: About your rephrased request... I think I know what you're getting at. In a way, you can "think" of structs as being internally immutable, that modifying a struct is that same as replacing it with a modified copy. It might even be what the CLR does internally in memory, for all I know. (That's how flash memory works. You cannot edit just a few bytes, you need to read a whole block of kilobytes into memory, modify the few you want, and write the whole block back.) However, even if they were "internally immutable", that is an implementation detail and for us developers as users of structs (their interface or API, if you will), they can be changed. We can't ignore that fact and "think of them as immutable".
In a comment you said "you cannot have a reference to the value of field or variable". You are assuming that every struct variable has a different copy, such that modifying one copy does not affect the others. That is not entirely true. The lines marked below are not replaceable if...
interface IFoo { DoStuff(); } struct Foo : IFoo { /* ... */ } IFoo otherFoo = new Foo(); IFoo foo = otherFoo; foo.DoStuff(whatEverArgumentsYouLike); // line #1 foo = foo.DoStuff(whatEverArgumentsYouLike); // line #2
Lines #1 and #2 do not have the same results... Why? Because
foo
andotherFoo
refer to the same boxed instance of Foo. Whatever is changed infoo
in line #1 reflects inotherFoo
. Line #2 replacesfoo
with a new value and does nothing tootherFoo
(assuming thatDoStuff()
returns a newIFoo
instance and does not modifyfoo
itself).Foo foo1 = new Foo(); // creates first instance Foo foo2 = foo1; // create a copy (2nd instance) IFoo foo3 = foo2; // no copy here! foo2 and foo3 refer to same instance
Modifying
foo1
won't affectfoo2
orfoo3
. Modifyingfoo2
will reflect infoo3
, but not infoo1
. Modifyingfoo3
will reflect infoo2
but not infoo1
.Confusing? Stick to immutable value types and you eliminate the urge of modifying any of them.
UPDATE: fixed typo in first code sample
这篇关于是值类型定义不变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!