C# 当同一类中的字段没有值时,为什么结构中的字段会丢失其值?

C# 当同一类中的字段没有值时,为什么结构中的字段会丢失其值?,c#,.net,struct,field,C#,.net,Struct,Field,我有一个结构,其中有一个字段失去了它的值。我可以声明字段为static,这就解决了问题。我还可以将struct更改为class(不更改任何其他内容),这也解决了问题。我只是想知道为什么会这样?结构是按值传递的。换句话说,当您传递一个结构时,您传递的是其值的副本。因此,如果您获取该值的副本并对其进行更改,则原始值将保持不变。你更改了副本,而不是原件 没有看到您的代码,我无法确定,但我想这就是正在发生的事情 类不会发生这种情况,因为它们是通过引用传递的 值得一提的是,这就是为什么结构应该是不可变的—

我有一个结构,其中有一个字段失去了它的值。我可以声明字段为static,这就解决了问题。我还可以将struct更改为class(不更改任何其他内容),这也解决了问题。我只是想知道为什么会这样?

结构是按值传递的。换句话说,当您传递一个结构时,您传递的是其值的副本。因此,如果您获取该值的副本并对其进行更改,则原始值将保持不变。你更改了副本,而不是原件

没有看到您的代码,我无法确定,但我想这就是正在发生的事情

类不会发生这种情况,因为它们是通过引用传递的

值得一提的是,这就是为什么结构应该是不可变的——也就是说,一旦它们被创建,它们就不会改变它们的值。提供修改版本的操作返回新结构


编辑:在下面的评论中,@supercat建议可变属性可以更方便。然而,结构上的属性设置器也会导致奇怪的失败。下面的示例可能会让您大吃一惊,除非您深入了解结构是如何工作的。对我来说,完全避免可变结构就足够了

考虑以下类型:

struct Rectangle {
    public double Left { get; set; }
}

class Shape {
    public Rectangle Bounds { get; private set; }
}
好的,现在想象一下这个代码:

myShape.Bounds.Left = 100;
也许令人惊讶的是,这根本没有效果!为什么?让我们以更长但等效的形式重新编写代码:

var bounds = myShape.Bounds;
bounds.Left = 100;
这里更容易看到如何将
Bounds
的值复制到局部变量,然后更改其值。但是,
Shape
中的原始值在任何时候都不会更新

这是使所有公共结构都不可变的有力证据。如果您知道自己在做什么,可变结构可能很方便,但就我个人而言,我实际上只将它们作为私有嵌套类使用

正如@supercat指出的,另一种选择有点难看:

myShape.Bounds = new Rectangle(100, myShape.Bounds.Top, 
                               myShape.Bounds.Width, myShape.Bounds.Height);
有时添加帮助器方法更方便:

myShape.Bounds = myShape.Bounds.WithLeft(100);

结构是按值传递的。换句话说,当您传递一个结构时,您传递的是其值的副本。因此,如果您获取该值的副本并对其进行更改,则原始值将保持不变。你更改了副本,而不是原件

没有看到您的代码,我无法确定,但我想这就是正在发生的事情

类不会发生这种情况,因为它们是通过引用传递的

值得一提的是,这就是为什么结构应该是不可变的——也就是说,一旦它们被创建,它们就不会改变它们的值。提供修改版本的操作返回新结构


编辑:在下面的评论中,@supercat建议可变属性可以更方便。然而,结构上的属性设置器也会导致奇怪的失败。下面的示例可能会让您大吃一惊,除非您深入了解结构是如何工作的。对我来说,完全避免可变结构就足够了

考虑以下类型:

struct Rectangle {
    public double Left { get; set; }
}

class Shape {
    public Rectangle Bounds { get; private set; }
}
好的,现在想象一下这个代码:

myShape.Bounds.Left = 100;
也许令人惊讶的是,这根本没有效果!为什么?让我们以更长但等效的形式重新编写代码:

var bounds = myShape.Bounds;
bounds.Left = 100;
这里更容易看到如何将
Bounds
的值复制到局部变量,然后更改其值。但是,
Shape
中的原始值在任何时候都不会更新

这是使所有公共结构都不可变的有力证据。如果您知道自己在做什么,可变结构可能很方便,但就我个人而言,我实际上只将它们作为私有嵌套类使用

正如@supercat指出的,另一种选择有点难看:

myShape.Bounds = new Rectangle(100, myShape.Bounds.Top, 
                               myShape.Bounds.Width, myShape.Bounds.Height);
有时添加帮助器方法更方便:

myShape.Bounds = myShape.Bounds.WithLeft(100);

当一个结构按值传递时,系统将为被调用方创建一个结构副本,这样它就可以看到它的内容,也可以修改它自己的副本,但不能影响调用方副本中的字段。还可以通过
ref
传递结构,在这种情况下,被调用方将能够使用调用方的结构副本,根据需要对其进行修改,甚至可以通过
ref
将其传递给其他类似的函数。请注意,被调用函数使调用方的结构副本可供其他函数使用的唯一方法是通过
ref
传递它,并且被调用函数在通过
ref
传递结构的所有函数返回之前不能返回。因此,可以向调用者保证,在函数调用返回时,结构可能发生的任何更改都已经发生

这种行为不同于类对象;如果一个函数将一个可变类对象传递给另一个函数,则它无法知道该另一个函数是否或何时会导致该对象立即或在将来的任何时候发生变化,即使该函数已完成运行。确保任何可变对象不会被外部代码变异的唯一方法是,从对象创建的那一刻起,直到对象被放弃为止,始终是该对象的唯一持有者

虽然不习惯值语义的人最初可能会“惊讶”于这样一个事实:逐值传递一个结构只会给被调用函数一个它的副本,而将一个结构存储位置分配给另一个结构存储位置只会复制结构的内容,但值类型提供的保证可能非常有用。由于
Point
是一种结构,因此可以知道像
MyPoints[5]这样的语句
(假设
MyPoints
是一个数组)将影响
MyPoints[5].X
,但不会影响任何其他
点。可以进一步确定的是,
MyPoints[5].X
将改变的唯一方式是,如果
MyPoints
被另一个数组替换,或者有东西写入
MyPoints[5]
。相比之下,
Point
是一个类,
MyPoint[5]