I cannot understand the output of the two sets of code snippets given below.
How don't really get the concept of shallow copy. How can it be explained?
Class:
public class Person : ICloneable
{
public string Name;
public int[] arr;
public object Clone()
{
return this.MemberwiseClone();
}
}
Code Snippet 1:
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "Name1";
p1.arr = new int[5] {1,2,3,4,5 };
Person p2 = (Person)p1.Clone();
p1.Name = "Name2";
p1.arr[0] = 11;
Console.WriteLine(p2.Name);
Console.WriteLine(p2.arr[0].ToString());
Console.Read();
}
Output:
Name1
11
Doubt: Isn't string a reference type. Then why p2.Name is printed as "Name1" in snippet 1
Code Snippet 2:
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "Name1";
p1.arr = new int[5] { 1, 2, 3, 4, 5 };
Person p2 = (Person)p1.Clone();
p1.Name = "Name2";
p1.arr = new int[5] { 11, 12, 13, 14, 15 };
Console.WriteLine(p2.Name);
Console.WriteLine(p2.arr[0].ToString());
Console.Read();
}
Output:
Name1
1
The int[] array in your example is a reference type. That means, that both p1.arr and p2.arr point to the same array in memory.
If you change the value of the first index of p1.arr, this means that the value of the first index of p2.arr is also changed. Hence, the behaviour in code snippet 1.
The difference in the second code snippet, is that you change the reference to the array of p1. Now, p1.arr is a reference to a new object. p2.arr still holds a reference to the 'original' array. Thus, printing p2.arr[0] prints 1.
EDIT:
To hopefully take away some doubt, maybe it is clearer if you remember that typing:
p1.Name = "Name2";
is actually:
p1.Name = new String("Name2");
This is exactly the same as with your int[] array. You are not changing the value of p1.Name, you are creating a new string object, and changing the reference of p1.Name to this new string object. p2.Name still holds its own reference to the 'original' string object, namely 'Name1'. By changing the reference of p1.Name, that reference does not change.
From http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
The MemberwiseClone method creates a shallow copy by creating a new object, and then copying the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
To alleviate your doubt in the original question.
String is indeed a reference type, The thing to remember is that what you are doing to the array and what you are doing to the string are not the same.
p1.Name = "Name2"; // new String -equivalent to p1.Name = new string("Name2")
p1.arr[0] = 11; //updated array element
For the array you are are changing the data in piece of memory referenced. For the String you are creating a new string ( at a new memory location) and making p1.Name ,the reference, point to that newly allocated memory. p2.Name (which is a different reference) remains pointing at the original memory location where the characters "Name1" are stored
As an aside because of the immutability of string there is no way a change to p1.Name will show up in p2.Name. Any attempt to change the string ,such as string.replace, will create a new string in memory.
hope that helps.
Pls see inline comments:
static void Main(string[] args)
{
Person p1 = new Person();
p1.Name = "Name1";
p1.arr = new int[5] {1,2,3,4,5 };
Person p2 = (Person)p1.Clone();
p1.Name = "Name2"; //Now p1.Name points to a new memory location
//But p2.Name is still pointing to the location p1.Name had
// originally pointed to.
p1.arr[0] = 11; //here p1.arr and p2.arr are pointing to the same place
//So since you are changing the value of one location it gets
//reflected in both
Console.WriteLine(p2.Name); //Prints Name1
Console.WriteLine(p2.arr[0].ToString()); //Prints 11
Console.Read();
}
In the second snippet when you say
p1.arr = new int[5] { 11, 12, 13, 14, 15 };
p1.arr is made to point to an entirely new location. (like what happens when you do p1.Name = "Name2") So it is not getting reflected on p2.arr which is still pointing to the same place p1.arr was previously pointing to. (i.e to the array {1,2,3,4,5})
Related
I would like to know what happens when we create an object "a", then make a reference "b" to it and then we create a new object with "a", what happens to 56? How I understand it that "a" loses its reference to 56 creating a new reference to 20. Making "b" the only holder of reference to 56.
class SingleDigit
{
public int Digit { get; set; }
public SingleDigit(int b)
{
Digit = b;
}
}
SingleDigit a = new SingleDigit(56); // creat new object with int 56
SingleDigit b = a; // make a reference to a
b.Digit -= 20; // both a and b's digit is subtracted
a = new SingleDigit(20); // What happens here ? Does a lose its reference to 56 ?
//Create a reference location for variable a. Assign it value 56.
SingleDigit a = new SingleDigit(56);
// Create a new object b and give it reference of a's object location.
//So, b.Digit is also 56.
SingleDigit b = a;
//Decrement 20 from b.Digit.
//It decrements from a.Digit as well since both
//refer to same memory object. So, both become 36.
b.Digit -= 20; // both a and b's digit is subtracted
// b holds up to original memory location.
//A new memory location has a new object created and
//its location is provided to variable a.
//a.Digit is now 20, and not 36.
a = new SingleDigit(20); //a loses its reference to 36
Please refer to excellent resources mentioned in comments !
So that you can visualize what's happening (an oversimplified view off course) have a look at the image below :
At first, both a and b refer to same memory location and value they refer to is 56.
When you subtract 20 the value at this particular memory location becomes 36.
Finally when you do
a = new SingleDigit(20);
a starts pointing to a new memory locations which contains value 20
For example i have the following class:
class Person
{
public List<int> Grades{ get; set; }
public Person(List<int> grades)
{
this.Grades = grades;
}
}
And than i use this class in a main method , something like:
static void Main(string[] args)
{
List<int> grades = new List<int> { 1, 2, 3, 4, 5 };
Person person = new Person(grades);
for (int i = 0; i < 5; i++)
{
if (i % 2 == 0)
{
grades[i] = 0;
}
else
{
grades[i] = -1;
}
}
foreach(var grade in person.Grades)
{
Console.Write(grade + " ");
}
Console.ReadKey();
}
After running this code i expected to have on console the:
1 2 3 4 5
output result.
Insteead i have this output result:
0 -1 0 -1 0
I expected that the collection "saved" into Person instance to stay how it was initialized. What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.
Can someone please explain me, why is this happening ?
I expected that the collection "saved" into Person instance to stay how it was initialized.
It's time to revisit your expectations of how reference types work in C# then :) I have an article that you might find useful...
What should i do to keep this collection unmodified for Person instances, so that i will have the "1 2 3 4 5" output result even if i modify the grades collection in main method.
You can copy the collection in the constructor:
public Person(List<int> grades)
{
this.Grades = new List<int>(grades);
}
Now for a List<int> that's fine - if you have a list of a mutable type, however, you potentially want to clone each element too. This sort of thing is why immutable types are nice - it's much easier to reason about their behaviour, because nothing can mess with instances, so you can just keep references...
Both the local variable grades and the field in the Person class are both referencing the same list. Evaluating either variable to its values result in the same list being returned, therefore mutating that one list that exists creates changes that are observable from either variable.
If you create a new list and copy all of the values from it to the new list when assigning the property's value in the constructor then you will have two separate lists, and mutating one won't be observable through the other.
this.Grades = grades;
The above line only assign the reference held by grades to this.Grades so now both are pointing/referencing the same item in memory.
To resolve it, since it is a List<int>, you can do
this.Grades = grades.ToList();
But if you have List<T> where T is a class or a reference type, you will still have the same problem. See: How create a new deep copy (clone) of a List<T>?
Lists are reference objects.
When you pass the list to the new person object, what you're really doing is passing it a pointer to the list, so the list in the person and the local list variable grades are actually the same list.
You can have the person object create a copy of the list instead using either of these approaches:
this.Grades = grades.ToList();
// or
this.Grades = new List<int>(grades);
Could someone please explain the behavior of this
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
struct person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 1 - 23
Person - 2 - 24
*/
Why am I not getting the same output for a class as that of a struct?
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
class person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 2 - 24
Person - 2 - 24
*/
Classes are reference types, structs are value types.
When a value type is passed to a method as a parameter, a copy of it will be passed through. That means that you add two completely separate copies of the Person struct, one for each pass in the loop.
When a reference type is passed to a method as a parameter, the reference will be passed through. That mean that you add two copies of the reference to the same memory location (to the same Person object) - when making changes to this one object, you see it reflected in both references since they both reference the same object.
It's the difference between value type (struct) and reference type (class).
When you're adding the struct to Listperson the content of person is put in the list, you have two different person struct in your list.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains a person struct with value { age = 23, name = 1}
Second iteration:
Listperson contains a person struct with value { age = 23, name = 1}
Listperson contains another person struct with value { age = 24, name = 2}
*/
}
When you're adding the class the reference is put in the list, you have two references that referenced the same person object.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains 1 reference to myperson object with value { age = 23, name = 1}
Second iteration:
Listperson contains 2 reference to myperson object with value { age = 24, name = 2}
*/
}
Because your myperson variable only ever deals with one person struct/class.
What you add to the list, in your loop, is a copy of your myperson variable - which for the struct, will be an entire copy of the struct, but for the class will be a copy of the reference to the single instance that you create (and mutate).
If you want same result then bring person declaration inside of the for loop:-
// person myperson = new person();
//Move the upper line inside the for loop
for (int i = 1; i <= 2; i++)
{
person myperson = new person();
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
In struct you adding a value type hence separate values are stored, whereas in class you are adding reference to the object hence gettng same value.
In the second instance, you're addding a reference type. In fact, you're adding the same item, twice, since your
= new person()
is not in the loop. So it always points to the same object you initialized here:
person myperson = new person();
Even after it's added to your list, the changes affect it.
In the first instance, you're adding a struct each time, which is a value type, so will be copied into the list. Changes you make after that no longer refer to the object in the list, so they have different values.
Structures are value types and classes are reference types. So in your first example when you add myperson to the list your adding a copy of myperson and the myperson variable still refers to a separate copy. In you second example myperson is a reference type so your adding two pointers to the same object.
You should understand key distinctions between structs (Value Types) and classes (Reference Type). You could easily find this information in Google or at SO.
When you add struct instance to List you create another separate copy for this instance, and when you change one element you did not change another.
But in case of classes you create one instance and uses this one "shared" instance with two references (list[0] and list1) and you could change this one instance through two different references, that's why when you change list[0] item it seems that you change list1 item too.
Consider following code:
var s1 = new SampleStruct { X = 1, Y = 1 };
var s2 = s1;
//Creating separate copy
//Lets check this
Console.WriteLine(object.ReferenceEquals(s1, s2)); //Prints False
var c1 = new SampleClass { X = 1, Y = 2 };
var c2 = c1;
//We do not create any copy
// two references c1 and c2 "pointed" to one shared object
Console.WriteLine(object.ReferenceEquals(c1, c2)); //Prints True
Similar behavior we have when we pass parameter to function (or adding element to list).
In the second example you're only creating on item and adding a reference to to the list many times.
When you add the struct to the collection, it makes a copy of it. It's a value type. You'll end up with two distinct objects in the collection, each with different values. This is probably the expected behavior.
When you add the class, the reference type, to the collection, a new object is not created. You're actually adding two different references to the same object. You'll end up with (apparently) two objects with the same value. It's actually the same object, seemingly appearing twice in the collection.
Think of variables and parameters of class types as holding an "instance IDs". The only things one can actually do directly with an instance ID are (1) create a new one (which will be assigned to a new instance of a class), (2) assign one to another, or (3) check two IDs to see if they are equal. Doing anything else with a variable, parameter, etc. of a class type is a short hand for "do _ to the instance referred to this instance ID".
So code like:
{
Car A,B,C; /* Car is a class */
A = new Car;
B = new Car;
C = A;
A.color = carColors.Yellow;
B.color = C.color;
}
The first "new" statement will create an instance of Car and put its instance ID (let's say #1234) in "A". The second will create another car instance (#4321) and store its ID in B. The next statement will copy #1234 into C. It doesn't do anything with the car--it just copies the ID. Then car #1234 will be painted yellow, then in the last statement, the color of car #1234 (i.e. yellow) will be used to paint car #4321. Note that while A and C are different variables, they both hold the same instance ID (#1234) and thus refer to the same car.
Note: This applies to both List and ArrayList
Take a look at the following simple code:
class Creature
{
public string Name;
}
class Game
{
// This is a reference type
public Creature CurrentCreature;
}
class Program
{
static void Main(string[] args)
{
// First, we'll create 2 objects and let the
// reference type "CurrentCreature" points to one of them
Creature dragon = new Creature();
dragon.Name = "Dragon";
Creature Unicorn = new Creature();
dragon.Name = "Unicorn";
Game game = new Game();
game.CurrentCreature = dragon;
// Now we'll create a list which will contain
// the reference type "CurrentCreature"
List<Creature> list = new List<Creature>();
list.Add(game.CurrentCreature);
foreach (Creature c in list)
{
Console.WriteLine(c.Name); // Output is "Dragon"
}
// Now, we'll let "CurrentCreature" point to a different object
game.CurrentCreature = unicorn;
// The reference in the list still pointing to the original object!!!
foreach (Creature c in list)
{
Console.WriteLine(c.Name); // Output is "Dragon"!!!
}
Console.ReadLine();
}
}
I checked how a list adds an item and there is no instantiation of a new object. This is List.Add method (using Reflector tool)
public void Add(T item)
{
if (this._size == this._items.Length)
{
this.EnsureCapacity(this._size + 1);
}
this._items[this._size++] = item; // No instantiation
this._version++;
}
So, why is this happenning? The element in the list should be a reference to the object pointed to by "CurrentCreature" or is it not? Isn't it similar to the following code if we remove the list?:
class A
{
public B X;
}
class B
{
public string Name;
}
....
A a = new A();
B b1 = new B(); b1.Name = "b1";
B b2 = new B(); b2.Name = "b2";
a.X = b1;
Console.WriteLine(a.X.Name); // output: b1
b1 = b2;
Console.WriteLine(a.X.Name); // output: b1
When you do
game.CurrentCreature = unicorn;
You overwrite the pointer in game.CurrentCreature with one to the unicorn object. The array still has a pointer to the dragon object. It shouldn't be changed, this is how pointers work.
Edit:
A little explanation of what happens with the pointers:
First you created 2 objects
Creature dragon = new Creature();
dragon.Name = "Dragon";
Creature Unicorn = new Creature();
dragon.Name = "Unicorn";
This made dragon have a pointer to your dragon object, and unicorn have a pointer to your unicorn object.
Then you set the pointer of game.CurrentCreature to dragon's pointer.
game.CurrentCreature = dragon;
Then you add a pointer to dragon, the current creature, to the list
List<Creature> list = new List<Creature>();
list.Add(game.CurrentCreature);
Then you replace the pointer in game.CurrentCreature(was dragon) with a pointer to the unicorn object.
game.CurrentCreature = unicorn;
This will in no way affect the pointer held in the dragon object.
Cheers,
Reference types don't just change.
You're adding the creature to the list, not the game. Then you change the game's reference to use anoter creature; but the creature referenced in the list remains the same, thus it outputs the same result again.
Reference is copied to a list, not an object. After list.Add(myobject); you get two references (that are referring to the same object): myobject and those one that is in a list.
Could someone please explain the behavior of this
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
struct person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 1 - 23
Person - 2 - 24
*/
Why am I not getting the same output for a class as that of a struct?
class testCompile
{
/*
* Sample Code For Purpose of Illustration
*/
class person
{
public int age;
public string name;
}
static void Main(string[] args)
{
List<person> Listperson = new List<person>();
person myperson = new person();
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
int x = 0;
while (x < Listperson.Count)
{
//Output values
Console.WriteLine("{0} - {1}", Listperson[x].name, Listperson[x].age);
x++;
}
}
}
/*
Output:
Person - 2 - 24
Person - 2 - 24
*/
Classes are reference types, structs are value types.
When a value type is passed to a method as a parameter, a copy of it will be passed through. That means that you add two completely separate copies of the Person struct, one for each pass in the loop.
When a reference type is passed to a method as a parameter, the reference will be passed through. That mean that you add two copies of the reference to the same memory location (to the same Person object) - when making changes to this one object, you see it reflected in both references since they both reference the same object.
It's the difference between value type (struct) and reference type (class).
When you're adding the struct to Listperson the content of person is put in the list, you have two different person struct in your list.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains a person struct with value { age = 23, name = 1}
Second iteration:
Listperson contains a person struct with value { age = 23, name = 1}
Listperson contains another person struct with value { age = 24, name = 2}
*/
}
When you're adding the class the reference is put in the list, you have two references that referenced the same person object.
for (int i = 1; i <= 2; i++)
{
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
/* First time:
Listperson contains 1 reference to myperson object with value { age = 23, name = 1}
Second iteration:
Listperson contains 2 reference to myperson object with value { age = 24, name = 2}
*/
}
Because your myperson variable only ever deals with one person struct/class.
What you add to the list, in your loop, is a copy of your myperson variable - which for the struct, will be an entire copy of the struct, but for the class will be a copy of the reference to the single instance that you create (and mutate).
If you want same result then bring person declaration inside of the for loop:-
// person myperson = new person();
//Move the upper line inside the for loop
for (int i = 1; i <= 2; i++)
{
person myperson = new person();
//Assignment
myperson.age = 22+i;
myperson.name = "Person - " + i.ToString();
Listperson.Add(myperson);
}
In struct you adding a value type hence separate values are stored, whereas in class you are adding reference to the object hence gettng same value.
In the second instance, you're addding a reference type. In fact, you're adding the same item, twice, since your
= new person()
is not in the loop. So it always points to the same object you initialized here:
person myperson = new person();
Even after it's added to your list, the changes affect it.
In the first instance, you're adding a struct each time, which is a value type, so will be copied into the list. Changes you make after that no longer refer to the object in the list, so they have different values.
Structures are value types and classes are reference types. So in your first example when you add myperson to the list your adding a copy of myperson and the myperson variable still refers to a separate copy. In you second example myperson is a reference type so your adding two pointers to the same object.
You should understand key distinctions between structs (Value Types) and classes (Reference Type). You could easily find this information in Google or at SO.
When you add struct instance to List you create another separate copy for this instance, and when you change one element you did not change another.
But in case of classes you create one instance and uses this one "shared" instance with two references (list[0] and list1) and you could change this one instance through two different references, that's why when you change list[0] item it seems that you change list1 item too.
Consider following code:
var s1 = new SampleStruct { X = 1, Y = 1 };
var s2 = s1;
//Creating separate copy
//Lets check this
Console.WriteLine(object.ReferenceEquals(s1, s2)); //Prints False
var c1 = new SampleClass { X = 1, Y = 2 };
var c2 = c1;
//We do not create any copy
// two references c1 and c2 "pointed" to one shared object
Console.WriteLine(object.ReferenceEquals(c1, c2)); //Prints True
Similar behavior we have when we pass parameter to function (or adding element to list).
In the second example you're only creating on item and adding a reference to to the list many times.
When you add the struct to the collection, it makes a copy of it. It's a value type. You'll end up with two distinct objects in the collection, each with different values. This is probably the expected behavior.
When you add the class, the reference type, to the collection, a new object is not created. You're actually adding two different references to the same object. You'll end up with (apparently) two objects with the same value. It's actually the same object, seemingly appearing twice in the collection.
Think of variables and parameters of class types as holding an "instance IDs". The only things one can actually do directly with an instance ID are (1) create a new one (which will be assigned to a new instance of a class), (2) assign one to another, or (3) check two IDs to see if they are equal. Doing anything else with a variable, parameter, etc. of a class type is a short hand for "do _ to the instance referred to this instance ID".
So code like:
{
Car A,B,C; /* Car is a class */
A = new Car;
B = new Car;
C = A;
A.color = carColors.Yellow;
B.color = C.color;
}
The first "new" statement will create an instance of Car and put its instance ID (let's say #1234) in "A". The second will create another car instance (#4321) and store its ID in B. The next statement will copy #1234 into C. It doesn't do anything with the car--it just copies the ID. Then car #1234 will be painted yellow, then in the last statement, the color of car #1234 (i.e. yellow) will be used to paint car #4321. Note that while A and C are different variables, they both hold the same instance ID (#1234) and thus refer to the same car.