Reputation: 485
I have read about boxing in Richter's book and there is one thing I don't understand.
I have successfully changed struct
in object. But when I try to change struct
in collection, I have a problem.
//my struct
internal struct Point:IChangeBoxedPoint
{
public Int32 x, y;
public void Change(Int32 x, Int32 y)
{
this.x = x;
this.y = y;
}
public override string ToString()
{
return String.Format("{0}, {1}", this.x, this.y);
}
}
public static void Main()
{
List<Point> a = new List<Point>();
Point p = new Point();
p.Change(1, 1);
Console.WriteLine(p); //write 1, 1
object o = p;
Console.WriteLine(o); //write 1, 1
((Point)o).Change(2, 2);
Console.WriteLine(o); //write 1, 1
((IChangeBoxedPoint)o).Change(3, 3);
Console.WriteLine(o); //write 3, 3
for (int i = 0; i < 10; i++)
{
p.x = p.y = i;
a.Add(p);
}
Console.WriteLine(a[0]); //write 0, 0
((IChangeBoxedPoint)a[0]).Change(300,300);
Console.WriteLine(a[0]); //still writes 0,0
}
Upvotes: 4
Views: 158
Reputation: 50149
This is happening because struct
s are value types like primitives (int
, short
, etc.). When you box the struct
in object
it makes a copy of itself that works separately. Here is a simple example that illustrates this.
Point a = new Point();
Console.WriteLine(a); // 0, 0
a.Change(1, 1);
Console.WriteLine(a); // 1, 1
object b = a;
Console.WriteLine(b); // 1, 1
a.Change(2, 2);
Console.WriteLine(a); // 2, 2
Console.WriteLine(b); // 1, 1
class
vs struct
If you want to use a reference type you won't run into this issue and your program will just pass around the reference to your single object. Use a class
not struct
if you want a reference type.
internal class Point : IChangeBoxedPoint
According to this other question which references MSDN.
Do not define a structure unless the type has all of the following characteristics:
- It logically represents a single value, similar to primitive types (integer, double, and so on).
- It has an instance size smaller than 16 bytes.
- It is immutable.
- It will not have to be boxed frequently.
In case you didn't know immutable means the object can't change. So you're actually going against good practice by boxing the struct
s in an interface
in an attempt to change them.
IChangeBoxedPoint
You can get around your issue by using a List<IChangeBoxedPoint>
but this will just change it to use a reference to your value type, my question is why would you bother when you can just change your struct
to a class
.
List<IChangeBoxedPoint> a = new List<IChangeBoxedPoint>();
Upvotes: 3
Reputation: 8107
It seems to be right:
Console.WriteLine(a[0]); //write 0, 0 -
just because your first item received i=0
And for this code,
((IChangeBoxedPoint)a[0]).Change(300,300);
Does not actually change first item. It created new instance of boxed object Point, and changed its values.
Try to change it to be
Point newPoint = new Point();
newPoint.Change(300,300);
a[0] = newPoint;
Console.WriteLine(a[0]);
Upvotes: 0
Reputation: 4423
You should declare your collection as List<IChangeBoxedPoint>
instead of List<Point>
.
If you declare your list as List<Point>
then your values are boxed/unboxed to and from the interface, as others have already stated. But if you have a list of interfaces then values are boxed only when you add them to the list, allowing you to change them as needed.
Upvotes: 3