views:

194

answers:

4

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

+2  A: 

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.

Fabian Vilers
+4  A: 

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.

Razzie
pls check the doubt in the edited question.
+2  A: 

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.

TygerKrash
hi, i think you should have put the line p1.arr[0] = 11;for the explanation. Otherwise it may be misleading.
Mahatma
i meant your code snippet line p1.arr = new int[5] { 11, 12, 13, 14, 15 }; //updated arraydoesn't go with your explanation
Mahatma
Thanks Mahatma, you are correct of course I copied the array update snippet from the wrong part of the OPs post.
TygerKrash
A: 

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})

Mahatma