是值类型定义不变? [英] Are value types immutable by definition?

查看:139
本文介绍了是值类型定义不变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经常读到结构取值应该是一成不变的 - ?是不是他们定义



你?考虑 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 structs 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 to i. 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 with Point.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 that p = p.Offset(3, 4); expresses immutability much better than p.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 include ref parameters and boxing - so that it is not possible to rewrite all occurences of

foo.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. Both X and Y 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}". Here p1 was not modified because SetX() modified p2 which is a copy of p1. This happens because p1 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 calling SetX(). 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 a Point, but it was a copy of readOnlyPoint, 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 new Point, and you would not have been able to assign it to readOnlyPoint. 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 and otherFoo refer to the same boxed instance of Foo. Whatever is changed in foo in line #1 reflects in otherFoo. Line #2 replaces foo with a new value and does nothing to otherFoo (assuming that DoStuff() returns a new IFoo instance and does not modify foo 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 affect foo2 or foo3. Modifying foo2 will reflect in foo3, but not in foo1. Modifying foo3 will reflect in foo2 but not in foo1.

Confusing? Stick to immutable value types and you eliminate the urge of modifying any of them.


UPDATE: fixed typo in first code sample

这篇关于是值类型定义不变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆