Puzzling Collections

S

Sue & Bill

I attach the following code and output. I Add an object to a
Collection. I modified a property of the object. The item in the
Collection gets modifed also. Can you help me determine where I have
gone wrong. Thanks.


public class Shape
{
protected Size _size;
protected Point _location;

public Size Size
{
get{return _size;}
set{_size=value;}
}

public Point Location
{
get{return _location;}
set{_location=value;}
}

public Shape()
{ }
}


public class Rect : Shape
{
public Rect(Point loc, Size size) : base()
{
_size = size;
_location = loc;
}
}

// For second example
public class ShapeCollection : CollectionBase
{
public ShapeCollection() : base()
{ }

public void Add(Shape s)
{ this.List.Add(s); }

public Shape this[int index]
{
get
{ return (Shape)List[index]; }

set
{ List[index]=value; }
}
}

public class RectanglesCollection : CollectionBase
{
public RectanglesCollection() : base()
{ }
public void Add(Rectangle r)
{ this.List.Add(r); }

public Rectangle this[int index]
{
get
{ return (Rectangle)List[index]; }
set
{ List[index]=value; }
}
}

public void test()
{
ShapeCollection shapes = new ShapeCollection();
Rect rect = new Rect (new Point(0,0), new Size(50,50));
shapes.Add(rect);
Debug.WriteLine("1st item: " + shapes[0].Location.ToString());

rect.Location=new Point(2,2); //Change .Location property
Debug.WriteLine("1st item again: " +
shapes[0].Location.ToString() + " Why?!");

shapes.Add(rect);

foreach (Shape sh in shapes) Debug.WriteLine
(sh.Location.ToString());

Debug.WriteLine("==============");

RectanglesCollection rectanglesCollection = new
RectanglesCollection();
Rectangle r = new Rectangle(new Point(0,0), new Size(100,50));
rectanglesCollection.Add(r);
r.Location = new Point(5,5); // change the .Location property
Debug.WriteLine("List item after r modified: " +
rectanglesCollection[0].ToString());
rectanglesCollection.Add(r);
foreach (Rectangle rx in rectanglesCollection)
Debug.WriteLine(rx.ToString());
}


Output:
-------

1st item: {X=0,Y=0}
1st item again: {X=2,Y=2} Why?!
{X=2,Y=2}
{X=2,Y=2}
==============
List item after r modified: {X=0,Y=0,Width=100,Height=50}
{X=0,Y=0,Width=100,Height=50}
{X=5,Y=5,Width=100,Height=50}
 
G

Guest

Hi Sue & Bill,
you have done nothing wrong this is the expected behaviour that you should
get. Basically in .Net there are two main types of variables: Value and
References. Value variables get copied when assigned i.e.

int i = 3;
int j = i;
j += 1;

--> now i is still 3 and j is 4

This is because value types get copied when you pass them round - I think
this is what you were expecting to happen in your code below.

However, the other type, reference types do not get copied. This applies to
all objects, which are reference types (strings behave a little bit
differently, but we will not go into that here). In this case your variable
does not contain the actual data but really just a reference to the object
i.e.

MyClass c1 = new MyClass();
c1.Name = "c1";
MyClass c2 = c1;
c2.Name = "c2"

--> Now both c1.Name and c2.Name will return "c2"

c1,c2 are referencing the same instance of the MyClass object

c1 ------
\
---------------- MyClass Instance
c2 ------/


When you add an item to a collection you are really just passing a reference
to the object, not the actual object itself, so if you modify the object's
state in any way then when you access the reference in the collection which
is pointing to the modified object it will obviously show the modified state.


If you do not want the item in the collection to get modified you should
create a clone of the object before you put it intto the collection - this is
a duplicate copy of the object, look into the ICloneable interface.


Hope that helps
Mark R Dawson

Sue & Bill said:
I attach the following code and output. I Add an object to a
Collection. I modified a property of the object. The item in the
Collection gets modifed also. Can you help me determine where I have
gone wrong. Thanks.


public class Shape
{
protected Size _size;
protected Point _location;

public Size Size
{
get{return _size;}
set{_size=value;}
}

public Point Location
{
get{return _location;}
set{_location=value;}
}

public Shape()
{ }
}


public class Rect : Shape
{
public Rect(Point loc, Size size) : base()
{
_size = size;
_location = loc;
}
}

// For second example
public class ShapeCollection : CollectionBase
{
public ShapeCollection() : base()
{ }

public void Add(Shape s)
{ this.List.Add(s); }

public Shape this[int index]
{
get
{ return (Shape)List[index]; }

set
{ List[index]=value; }
}
}

public class RectanglesCollection : CollectionBase
{
public RectanglesCollection() : base()
{ }
public void Add(Rectangle r)
{ this.List.Add(r); }

public Rectangle this[int index]
{
get
{ return (Rectangle)List[index]; }
set
{ List[index]=value; }
}
}

public void test()
{
ShapeCollection shapes = new ShapeCollection();
Rect rect = new Rect (new Point(0,0), new Size(50,50));
shapes.Add(rect);
Debug.WriteLine("1st item: " + shapes[0].Location.ToString());

rect.Location=new Point(2,2); //Change .Location property
Debug.WriteLine("1st item again: " +
shapes[0].Location.ToString() + " Why?!");

shapes.Add(rect);

foreach (Shape sh in shapes) Debug.WriteLine
(sh.Location.ToString());

Debug.WriteLine("==============");

RectanglesCollection rectanglesCollection = new
RectanglesCollection();
Rectangle r = new Rectangle(new Point(0,0), new Size(100,50));
rectanglesCollection.Add(r);
r.Location = new Point(5,5); // change the .Location property
Debug.WriteLine("List item after r modified: " +
rectanglesCollection[0].ToString());
rectanglesCollection.Add(r);
foreach (Rectangle rx in rectanglesCollection)
Debug.WriteLine(rx.ToString());
}


Output:
-------

1st item: {X=0,Y=0}
1st item again: {X=2,Y=2} Why?!
{X=2,Y=2}
{X=2,Y=2}
==============
List item after r modified: {X=0,Y=0,Width=100,Height=50}
{X=0,Y=0,Width=100,Height=50}
{X=5,Y=5,Width=100,Height=50}
 
S

Sue & Bill

Mark, thanks for the explanation.

Yes, I kow about value and reference types. That's why I included a
second example. The second example is also Adding objects to a
Collection but the result is different. Do you mind taking another
look.

(Errata: the "// For second example" remark should be one block down.)

Anyway, the total documentation on IList.Add says "When implemented by
a class, adds an item to the IList." It does not give the slightest
hint whether an item is Added by reference or by copying.

Thanks in advance.
 
G

Guest

Hi Sue & Bill,
ah - sorry just woken up and answered before eating my weetabix, always a
bad thing to do, now I have some sugar pumping around my veins I see more
clearly, I did not notice you were using Rect and Rectangle in your examples.

In your first example you add instances of your Rect class to the collection:

ShapeCollection shapes = new ShapeCollection();
Rect rect = new Rect (new Point(0,0), new Size(50,50));
shapes.Add(rect);
Console.WriteLine("1st item: " + shapes[0].Location.ToString());

rect.Location=new Point(2,2); //Change .Location property
Console.WriteLine("1st item again: " +shapes[0].Location.ToString() + "
Why?!");
shapes.Add(rect);

foreach (Shape sh in shapes)
Console.WriteLine(sh.Location.ToString());


Your rect class is passed as a refence into the collection, so you modify
the Location property to be 2,2 therefore the reference in the collection and
your rect variable are still pointing to the same object that is why you are
seeing:
{X=2,Y=2}
{X=2,Y=2}
in your output.

In your second example, you are adding Rectangle structures into the
collection. This type is a struct not a class, structs are passed by value
into the collection, i.e. a copy is created of the object when you add it to
the collection. So after you have added a COPY of the rectangle into the
collection and say:

r.Location = new Point(5,5);

you are changing the location of the r variable instance but not the COPY of
the rectangle which is now in the collection.

An easy way to tell if a class is a value type or reference type is to look
at the .Net documentation and see if the object inherits from
System.ValueType if so then it will have ValueType semantics, such as the
Rectangle class does.

You might want to look into Boxing and Unboxing as well to help you more in
this topic.

Hope that helps
Mark R Dawson.
 
R

Richard Blewett [DevelopMentor]

When you return a shape, this is a reference type so you actually return a reference to the object in the cvollection and this gets modified. When you return a Rectangle this is a value type and so a copy of the one in the collection is returned

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

Mark, thanks for the explanation.

Yes, I kow about value and reference types. That's why I included a
second example. The second example is also Adding objects to a
Collection but the result is different. Do you mind taking another
look.

(Errata: the "// For second example" remark should be one block down.)

Anyway, the total documentation on IList.Add says "When implemented by
a class, adds an item to the IList." It does not give the slightest
hint whether an item is Added by reference or by copying.

Thanks in advance.
 
S

Sue & Bill

Mark, thank you very much for the explanation. I get it now. Didn't
realize the subtle differences. Still new to C#.
 
S

Sue & Bill

Thanks to all.

I just realized that my original problem is still not solved. I want
to create a list and plug objects in them. I don't want to keep a copy
of each object outside the list, because they will be created at run
time and I won't know how to name them. Any suggestions for the most
appropriate method?

Thanks.
 
J

Jon Skeet [C# MVP]

J

Jon Skeet [C# MVP]

Sue & Bill said:
Thanks to all.

Glad the others have sorted you out with the earlier problem - sorry
about the reply I just posted to your original message. I should have
read the whole thread first :)
I just realized that my original problem is still not solved. I want
to create a list and plug objects in them. I don't want to keep a copy
of each object outside the list, because they will be created at run
time and I won't know how to name them. Any suggestions for the most
appropriate method?

Just add the reference to the list, and it'll be fine - there's no
concept of the object itself "belonging" to the list or to outside the
list - just the fact that the list has a reference to the object will
stop it from being garbage collected, if that's what you were worried
about. (If the list itself is eligible for garbage collection, the
objects referred to by the list is also eligible if there are no other
references to them.)
 
J

Jeff Louie

You may want a type based class factory.

http://www.geocities.com/Jeff_Louie/OOP/oop18.htm

Regards,
Jeff
I just realized that my original problem is still not solved. I want
to create a list and plug objects in them. I don't want to keep a copy
of each object outside the list, because they will be created at run
time and I won't know how to name them. Any suggestions for the most
appropriate method?<
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top