为什么C#号码类型是不可变的?

Nob*_*ody 18 c# types immutability

为什么ints和doubles不可变?每次要更改值时返回新对象的目的是什么?

我问的原因是因为我正在创建一个类:BoundedInt它有一个值和一个上限和下限.所以我想知道:我是否应该使这种类型不变?(或者它应该是struct?)

Dan*_*Tao 34

首先:

每次要更改值时返回新对象的目的是什么?

我想你可能会错误地认为价值类型是如何运作的.这可能不像你想象的那样昂贵的操作; 它只是覆盖数据(与例如新内存的动态分配相反).

其次:这是一个非常简单的例子,说明为什么数字是不可变的:

5.Increase(1);
Console.WriteLine(5); // What should happen here?
Run Code Online (Sandbox Code Playgroud)

当然,这是一个人为的例子.所以让我们考虑一些更复杂的想法.

可变参考类型

首先,有一个:如果Integer是一个可变的引用类型怎么办?

class Integer
{
    public int Value;
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以得到这样的代码:

class Something
{
    public Integer Integer { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和:

Integer x = new Integer { Value = 10 };

Something t1 = new Something();
t1.Integer = x;

Something t2 = new Something();
t2.Integer = t1.Integer;

t1.Integer.Value += 1;

Console.WriteLine(t2.Integer.Value); // Would output 11
Run Code Online (Sandbox Code Playgroud)

这似乎违背了直觉:线条t2.Integer = t1.Integer只会复制一个值(实际上,它确实存在;但"值"实际上是一个参考),因此t2.Integer它将保持独立t1.Integer.

可变值类型

当然,这可以采用另一种方式,保持Integer作为值类型但保持其可变性:

struct Integer
{
    public int Value;

    // just for kicks
    public static implicit operator Integer(int value)
    {
        return new Integer { Value = value };
    }
}
Run Code Online (Sandbox Code Playgroud)

但现在让我们说我们这样做:

Integer x = 10;

Something t = new Something();
t.Integer = x;

t.Integer.Value += 1; // This actually won't compile; but if it did,
                      // it would be modifying a copy of t.Integer, leaving
                      // the actual value at t.Integer unchanged.

Console.WriteLine(t.Integer.Value); // would still output 10
Run Code Online (Sandbox Code Playgroud)

基本上,价值观的不变性是非常直观的.相反的是非常不直观的.

我猜这是主观的,但公平地说;)

  • 上次编辑的改进很好.如果我能第二次投票(在所谓的芝加哥风格),我愿意. (2认同)

Rob*_*vey 7

作为一个可变对象,你必须int在更改它之前锁定一个变量(在从不同线程写入你的int的任何多线程代码中).

为什么?假设你正在增加一个int,就像这样:

myInt++
Run Code Online (Sandbox Code Playgroud)

在引擎盖下,这是一个32位数字.从理论上讲,在32位计算机上你可以加1,这个操作可能是原子的; 也就是说,它将在一个步骤中完成,因为它将在CPU寄存器中完成.不幸的是,它不是; 还有比这更多的事情.

如果另一个线程在增加中间时突变了这个数字怎么办?你的号码会被破坏.

但是,如果在递增对象之前创建对象的线程安全副本,请对线程安全副本进行操作,并在增量完成时返回新对象,保证增量是线程安全的.它不会受到原始对象上发生在其他线程上的任何操作的影响,因为您不再使用原始对象.实际上,您已使对象不可变.

这是函数式编程背后的基本原理; 通过使对象不可变,并从函数返回新对象,您可以免费获得线程安全.


Ste*_*dit 6

整数变量可变的。但是,整数文字是常量,因此是不可变的。

int i = 0;

// Mutation coming!
i += 3;

// The following line will not compile.
3 += 7;
Run Code Online (Sandbox Code Playgroud)

使用可以使整数字段不可变readonly。同样,整数属性可以是仅获取的。