I noticed something in C# when dealing with custom objects that I found to be a little odd. I am certain it is just a lack of understanding on my part so maybe someone can enlighten me.
If I create a custom object and then I assign that object to the property of another object and the second object modifies the object assigned to it, those changes are reflected in the same class that did the assigning even though nothing is returned.
You want that in English? Here is an example:
class MyProgram
{
static void Main()
{
var myList = new List<string>();
myList.Add("I was added from MyProgram.Main().");
var myObject = new SomeObject();
myObject.MyList = myList;
myObject.DoSomething();
foreach (string s in myList)
Console.WriteLine(s); // This displays both strings.
}
}
public class SomeObject
{
public List<string> MyList { get; set; }
public void DoSomething()
{
this.MyList.Add("I was added from SomeObject.DoSomething()");
}
}
In the above sample I would have thought that, because SomeObject.DoSomething()
returns void, this program would only display "I was added from MyProgram.Main()."
. However, the List<string>
in fact contains both that line and "I was added from SomeObject.DoSomething()"
.
Here is another example. In this example the string remains unchanged. What is the difference and what am I missing?
class MyProgram
{
static void Main()
{
var myString = "I was set in MyProgram.Main()";
var myObject = new SomeObject();
myObject.MyString = myString;
myObject.DoSomething();
Console.WriteLine(myString); // Displays original string.
}
}
public class SomeObject
{
public string MyString { get; set; }
public void DoSomething()
{
this.MyString = "I was set in SomeObject.DoSomething().";
}
}
This program sample ends up displaying "I was set in MyProgram.Main()"
. After seeing the results of the first sample I would have assumed that the second program would have overwritten the string with "I was set in SomeObject.DoSomething()."
. I think I must be misunderstanding something.
This isn't odd, or strange. When you create a class, you create reference type. When you pass references to objects around, modifications to the objects they refer to are visible to anyone that holds a reference to that object.
var myList = new List<string>();
myList.Add("I was added from MyProgram.Main().");
var myObject = new SomeObject();
myObject.MyList = myList;
myObject.DoSomething();
So in this block of code, you instantiate a new instance of List<string>
and assign a reference to that instance to the variable myList
. Then you add "I was added from MyProgram.Main()."
to the list referred to by myList
. Then you assign a refernce to that same list to myObject.MyList
(to be explicit, both myList
and myObject.MyList
are referring to the same List<string>
! Then you invoke myObject.DoSomething()
which adds "I was added from SomeObject.DoSomething()"
to myObject.MyList
. Since both myList
and myObject.MyList
are referring to the same List<string>
, they will both see this modification.
Let's go by way of analogy. I have a piece of paper with a telephone number on it. I photocopy that piece of paper and give it to you. We both have a piece of paper with the same telephone number on it. Now I call up that number and tell the person on the other end of the line to put a banner up on their house that says "I was added from MyProgram.Main()."
You call up the person on the other end of the line to put a banner up on their house that says "I was added from SomeObject.DoSomething()"
. Well, the person who lives at the house that has that telephone number is now going to have two banners outside their house. One that says
I was added from MyProgram.Main().
and another that says
I was added from SomeObject.DoSomething()
Make sense?
Now, in your second example, it's a little trickier.
var myString = "I was set in MyProgram.Main()";
var myObject = new SomeObject();
myObject.MyString = myString;
myObject.DoSomething();
You start by creating a new string
whose value is "I was set in MyProgram.Main()"
and assign a reference to that string to myString
. Then you assign a reference to that same string to myObject.MyString
. Again, both myString
and myObject.MyString
are referring to that same string
whose value is "I was set in MyProgram.Main()"
. But then you invoke myObject.DoSomething
which has this interesting line
this.MyString = "I was set in SomeObject.DoSomething().";
Well, now you've created a new string
whose value is "I was set in SomeObject.DoSomething()."
and assign a reference to that string
to myObject.MyString
. Note that you never changed the reference that myString
holds. So now, myString
and myObject.MyString
are referring to different strings!
Let's go by analogy again. I have a piece of paper with a web address on it. I photocopy that piece of paper and give it to you. We both have a piece of paper with the same web address on it. You cross out that web address and write down a different address. It doesn't affect what I see on my piece of paper!
Finally, a lot of people in this thread are yammering about the immutability of string
. What is going on here has nothing to do with the immutability of string
.
It's absolutely correct:
myObject.MyList = myList;
This line assign a reference of myList
to the myObject's property.
To prove this this, call GetHashCode()
on myList
and on myObject.MyList
.
we are talking about different pointers to same memory location, if you wish.
Whether or not a method returns something, has nothing to do with what happens inside it.
You seem to be confused regarding what assignment actually means.
Let's start from the beginning.
var myList = new List<string>();
allocates a new List<string>
object in memory and puts a reference to it into myList
variable.
There is currently just one instance of List<string>
created by your code but you can store references to it in different places.
var theSameList = myList;
var sameOldList = myList;
someObject.MyList = myList;
Right now myList
, theSameList
, sameOldList
and someObject.MyList
(which is in turn stored in a private field of SomeObject
automagically generated by compiler) all refer to the same object.
Have a look at these:
var bob = new Person();
var guyIMetInTheBar = bob;
alice.Daddy = bob;
harry.Uncle = bob;
itDepartment.Head = bob;
There is just one instance of Person
, and many references to it.
It's only natural that if our Bob grew a year older, each instance's Age
would have increased.
It's the same object.
If a city was renamed, you'd expect all maps to be re-printed with its new name.
You find it strange that
those changes are reflected in the same class that did the assigning
—but wait, changes are not reflected. There's no copying under the hood. They're just there, because it's the same object, and if you change it, wherever you access it from, you access its current state.
So it matters not where you add an item to the list: as long as you're referring to the same list, you'll see the item being added.
As for your second example, I see Jason has already provided you with a much better explanation than I could possibly deliver so I won't go into that.
It will suffice if I say:
string
for a variety of reasons.Even if they were mutable (like List<T>
that has its internal state modifiable via methods), in your second example, you're not changing the object, you're changing the reference.
var goodGuy = jack;
alice.Lover = jack;
alice.Lover = mike;
Would alice
's change of mood make jack
a bad guy? Certainly not.
Similarly, changing myObject.MyString
doesn't affect local variable myString
. You don't do anything to the string itself (and in fact, you can't).
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