为什么 C# 数字类型是不可变的?

发布于 2024-09-28 05:08:54 字数 167 浏览 3 评论 0原文

为什么 int 和 double 是不可变的?每次想要更改值时返回一个新对象的目的是什么?

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

Why are ints and doubles 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

屋檐 2024-10-05 05:08:54

首先:

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

我认为您可能对值类型的工作方式有误解。这并不像您想象的那样是一项昂贵的操作;它是一种成本高昂的操作。它只是数据的覆盖(与例如新内存的动态分配相反)。

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

5.Increase(1);
Console.WriteLine(5); // What should happen here?

当然,这是一个人为的例子。因此,让我们考虑一些更复杂的想法。

可变引用类型

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

class Integer
{
    public int Value;
}

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

class Something
{
    public Integer Integer { get; set; }
}

而且:

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

这似乎违反直觉: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 };
    }
}

但是现在假设我们这样做:

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

基本上,值的不变性> 是高度直观的东西。相反的情况是非常不直观的。

不过,平心而论,我想这是主观的;)

Firstly:

What is the purpose of returning a new object each time you want to change the value?

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:

5.Increase(1);
Console.WriteLine(5); // What should happen here?

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?

class Integer
{
    public int Value;
}

Then we could have code like this:

class Something
{
    public Integer Integer { get; set; }
}

And:

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

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 that t2.Integer would remain independent of t1.Integer.

Mutable value type

This could be approached another way, of course, keeping Integer as a value type but maintaining its mutability:

struct Integer
{
    public int Value;

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

But now let's say we do this:

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

Basically, immutability of values is something that is highly intuitive. The opposite is highly unintuitive.

I guess that is subjective, though, in all fairness ;)

埋情葬爱 2024-10-05 05:08:54

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

int i = 0;

// Mutation coming!
i += 3;

// The following line will not compile.
3 += 7;

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

Integer variables are mutable. However, integer literals are constants, hence immutable.

int i = 0;

// Mutation coming!
i += 3;

// The following line will not compile.
3 += 7;

It's possible to make an integer field immutable, using readonly. Likewise, an integer property could be get-only.

悟红尘 2024-10-05 05:08:54

作为可变对象,您必须在更改 int 变量之前锁定它(在从单独线程写入 int 的任何多线程代码中)。

为什么?假设您正在递增一个 int,如下所示:

myInt++

在幕后,这是一个 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:

myInt++

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.

老娘不死你永远是小三 2024-10-05 05:08:54

将 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.

枉心 2024-10-05 05:08:54

任何具有值语义的内容在 C# 中都应该是不可变的。

可变类不能具有值语义,因为您无法覆盖赋值运算符。

MyClass o1=new MyClass();
MyClass o2=o1;
o1.Mutate();
//o2 got mutated too
//=> no value but reference semantics

可变结构很丑陋,因为您可以轻松地对临时变量调用可变方法。特别是属性返回临时变量。

MyStruct S1;
MyStruct S2{get;set;}

S1.Mutate(); //Changes S1
S2.Mutate();//Doesn't change S2

这就是为什么我不喜欢大多数 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.

MyClass o1=new MyClass();
MyClass o2=o1;
o1.Mutate();
//o2 got mutated too
//=> no value but reference semantics

Mutable structs are ugly because you can easily call a mutating method on a temporary variable. In particular properties return temporary variables.

MyStruct S1;
MyStruct S2{get;set;}

S1.Mutate(); //Changes S1
S2.Mutate();//Doesn't change S2

That's why I don't like that most Vector libraries use mutating methods like Normalize in their Vector struct.

一场信仰旅途 2024-10-05 05:08:54

我正在研究神经网络的学术项目。这些网络使用双精度进行大量计算。我在亚马逊云上的 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文