装箱后改变结构

时间:2014-06-28 05:22:53

标签: c# boxing

我有以下代码:

//Interface defining a Change method
internal interface IChangeBoxedPoint
{
    void Change(Int32 x, Int32 y);
}

internal struct Point : IChangeBoxedPoint 
{
    private Int32 m_x, m_y;

    public Point(Int32 x, Int32 y)
    {
        m_x = x;
        m_y = y;
    }

    public void Change(Int32 x, Int32 y)
    {
        m_x = x; m_y = y;
    }

    public override string ToString()
    {
        return String.Format("({0}, {1})", m_x.ToString(), m_y.ToString());
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Point p = new Point(1, 1);

        Console.WriteLine(p); // "(1, 1)"

        p.Change(2, 2);
        Console.WriteLine(p); // "(2, 2)"

        Object o = p;
        Console.WriteLine(o); // "(2, 2)"

        ((Point)o).Change(3, 3);
        Console.WriteLine(o); // "(2, 2)"

        //Boxes p, changes the boxed object and discards it
        ((IChangeBoxedPoint)p).Change(4, 4);
        Console.WriteLine(p); // "(2, 2)"

        IChangeBoxedPoint h = ((IChangeBoxedPoint)p);
        h.Change(4, 4);
        //Boxed p is not yet garbage collected
        Console.WriteLine(p);// "(2, 2)"

        Console.WriteLine(h); //"(4, 4)" //After this line boxed p is garbage collected

        //Changes the boxed object and shows it
        ((IChangeBoxedPoint)o).Change(5, 5);
        Console.WriteLine(o); // "(5, 5)"

        Console.Read();
    }
}

这是CLR通过C#4th ed(第138页)的修改示例。 在书中它说的是

((IChangeBoxedPoint)p).Change(4, 4);
Console.WriteLine(p); // "(2, 2)"

将输出"(2,2)"因为盒装的p会立即被垃圾收集。 但是,实际的原因是,更改盒装变量的值只会改变堆上对象的值,在这种情况下,保持p不变?为什么收集垃圾的盒装p与它有关?

我认为它与它无关,因为在此代码中:

IChangeBoxedPoint h = ((IChangeBoxedPoint)p);
h.Change(4, 4);
//Boxed p is not yet garbage collected
Console.WriteLine(p);// "(2, 2)"

Console.WriteLine(h); // //After this line boxed p is garbage collected

我们正在拳击p,在盒装p上调用change方法,在Console.WriteLine(h);中使用它来保持h,但是Console.WriteLine(p);仍会输出"(2,2)&# 34。

有谁知道为什么作者写道盒装p被垃圾收集是p没有改变的原因,因为它似乎根本不是真的。

2 个答案:

答案 0 :(得分:3)

你描述它的方式,这本书是错误的*,垃圾收集过程根本不会影响过程。实际上,垃圾收集器每隔一段时间就会运行一次,它甚至在这两个语句中都很活跃。

原因是装箱会创建另一个结构副本,因此我们现在正在处理结构的两个实例。框中的原始ph。更改h后,原始p仍然不受影响。

C#中的结构具有值语义,这意味着它们在许多情况下会自动复制,例如赋值,作为参数传递给函数和装箱。通过修改副本而不是原始结构很容易出错。为避免这种情况,结构应始终是不可变的

*)根据@KirillShlenskiy的引用,我不能再说这本书是错的,因为它不是。这是正确的,但解释有点令人困惑,很容易被误解。

答案 1 :(得分:2)

一些句子的措辞似乎很差,很容易被误解。然而,稍后,作者明确指出:

  这个整体的目的就是举例说明如何   interface方法能够修改盒装值类型的字段。   在C#中,如果不使用接口方法,这是不可能的。

我非常肯定作者知道意外的结果来自界面的使用,甚至与可用于垃圾收集的对象密切相关。