I have the following code:
//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();
}
}
This is a modified example from CLR via C# 4th ed (pg 138). In the book it says that
((IChangeBoxedPoint)p).Change(4, 4);
Console.WriteLine(p); // "(2, 2)"
will output "(2, 2)" because the boxed p will be immediately garbage collected. However, wouldn't the actual reason for this be the fact that changing the value of a boxed variable only changes the value in the object on the heap, in this case, leaving p untouched? Why does the boxed p getting garbage collected have anything to do with it?
I believe it has nothing to do with it because in this code:
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
we are boxing p, calling the change method on the boxed p, keeping h alive by using it in Console.WriteLine(h);
, however Console.WriteLine(p);
will still output "(2, 2)".
Does anyone know why the author wrote that the boxed p being garbage collected is the reason why p is not changed, because it doesn't seem to be true at all.
The way you describe it, the book is wrong*, the garbage collection process does not affect the process at all. In fact, the garbage collector runs at intervals and it would be very rare for it to even be active between those two statements.
The reason is that the boxing creates another copy of the struct, so that we are now working with two instances of the struct. The original p
and the h
in the box. When h
is changed, the original p
remains unaffected.
Structs in C# have value semantics which means that they are automatically copied in many cases, such as assignment, passing as parameters to functions and boxing. It is very easy to make mistakes by modifying a copy instead of the original struct. To avoid that, structs should always be immutable.
*) With the quote from @KirillShlenskiy I can no longer say that the book is wrong because it isn't. It is correct, but the explanation is a bit confusing and can easily be misunderstood.
A couple of the sentences do appear to be poorly worded and can be easily misunderstood. However, later on, the author specifically states:
The purpose of this whole is example is to demonstrate how an interface method is able to modify the fields of a boxed value type. In C#, this isn't possible without using an interface method.
I'm quite certain the author knows that the unexpected results are from the use of the interface and not even closely related to the object becoming available to be garbage collected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With