我有以下代码:
//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没有改变的原因,因为它似乎根本不是真的。
答案 0 :(得分:3)
你描述它的方式,这本书是错误的*,垃圾收集过程根本不会影响过程。实际上,垃圾收集器每隔一段时间就会运行一次,它甚至在这两个语句中都很活跃。
原因是装箱会创建另一个结构副本,因此我们现在正在处理结构的两个实例。框中的原始p
和h
。更改h
后,原始p
仍然不受影响。
C#中的结构具有值语义,这意味着它们在许多情况下会自动复制,例如赋值,作为参数传递给函数和装箱。通过修改副本而不是原始结构很容易出错。为避免这种情况,结构应始终是不可变的。
*)根据@KirillShlenskiy的引用,我不能再说这本书是错的,因为它不是。这是正确的,但解释有点令人困惑,很容易被误解。
答案 1 :(得分:2)
一些句子的措辞似乎很差,很容易被误解。然而,稍后,作者明确指出:
这个整体的目的就是举例说明如何 interface方法能够修改盒装值类型的字段。 在C#中,如果不使用接口方法,这是不可能的。
我非常肯定作者知道意外的结果来自界面的使用,甚至与可用于垃圾收集的对象密切相关。