1

我知道 struct 是值类型,而 class 是引用类型,但是当我执行以下代码时,为什么我得到两个不同的答案

谁能解释一下

[struct|class] values {
    public int x, y;

    public values (int x, int y) {
        this.x = x;
        this.y = y;
    }
}

values v = new values(12, 13);
object o = v;
v.x = 2;
Console.WriteLine(((values)o).x);

输出

什么时候

  1. 它是类:输出为 2

  2. 它是结构:输出为 12

任何人都可以解释我吗?

谢谢

4

4 回答 4

9

行为非常不同的一条线structorclass

  object o = v;
  • Values是引用类型时,o成为引用的副本v。仍然只有 1 个实例Values

  • WhenValues是一个值类型,o成为对实例本身的盒装副本的引用。在这种情况下,分配创建了第二个实例,您v.x = 2在原始实例上执行。副本不受影响。

您的示例包括拳击,这是不必要的复杂性,当您使用时,values o = v;您将获得相同的输出。然后该行创建一个没有装箱的普通副本(第二个实例)。

总结一下:值和引用类型的主要区别在于复制语义。您会注意到简单赋值 ( x = y) 和参数传递 ( foo(x)) 中的不同行为。

您可以期待具有可变值类型的陷阱。作为一个练习,看看作为类或结构的 f.Myvalue.x = 2;作用是什么Values

 class Foo { public Values MyValue  { get; set; } }
于 2013-08-14T12:20:36.483 回答
1

当您使用结构并分配它(或将其用作方法的参数)时,您将拥有结构的全新副本。而对于一个类,您可以为该类分配一个引用。

于 2013-08-14T12:23:05.270 回答
1

给定

SomeStruct s1, s2, s3;
Object o1;
... s1 gets a value somehow, then...
s2 = s1;  // Q1
o1 = s1;  // Q2
s3 = (SomeStruct)o1; // Q3

语句 Q1 将s2通过使用s1. 语句 Q2 将生成一个新的堆对象,该对象的行为就好像它包含一个类型为 的字段SomeStruct,并将通过用 中的相应字段覆盖其所有字段来修改该字段s1。语句 Q3 将检查是否o1包含“装箱” SomeStruct,如果是,将用s3装箱对象中的相应字段覆盖 的所有字段。

请注意,盒装结构通常不会它们位于堆对象中时被修改。如果有人说:

var temp = (SomeStruct)o1;
temp.someField = Whatever;
o1 = temp;

该序列将创建一个新的堆对象,该对象将保存原始结构的修改副本,但原始盒装结构将保持原样。但是,有一些方法可以修改装箱对象中的结构。在大多数情况下应该避免使用这种技术,但应该知道不存在非平凡的不可变结构(没有字段的结构是不可变的,但不是很有用)。一般来说,如果希望能够引用结构类型的可变实例,最好将它封装在一个简单的类对象中,例如

class ExposedFieldHolder<T>
{
  public T Value; 
  public ExposedFieldHolder(T v) {Value = v;}
}

如果想要一个不可变的实例,应该将它封装在一个不可变的类对象中

class ImmutableHolder<T>
{
  T value;
  public T Value { get { return value; } ; 
  public ImmutableHolder(T v) {value = v;}
  override public bool Equals(Object o) {
    var other = o as ImmutableHolder<T>;
    if (!other) return false;
    return Object.Equals(value,other.value);
  }
  override public int GetHashCode() { return Object.GetHashCode(value); }
}
于 2013-08-14T19:56:35.347 回答
1

关键线是

object o = v;

当值是结构(或值类型)时,它会导致装箱值。根据这个(按照那个链接,你可以在最后找到你的问题:))

将值类型的值装箱包括分配对象实例并将值类型值复制到该实例中。

因此,您在 v 中的值是copied。当你在这里拆箱

Console.WriteLine(((values)o).x);

你得到原始v价值,而不是v之后

v.x = 2;

因此,结构(或值类型)的答案是12.

对于类(或引用类型),它非常简单。您不是拳击,只是投射到对象,所以接下来您将使用这个原件v并更改它的值。

于 2013-08-14T12:28:11.233 回答