Passing Value Type parameters to functions in c# is by value unless you use the ref or out keyword on the parameter. But does this also apply to Reference Types?
Specifically I have a function that takes an IList<Foo>. Will the list passed to my function be a copy of the list with copy of its contained objects? Or will modifications to the list also apply for the caller? If so - Is there a clever way I can go about passing a copy?
public void SomeFunction()
{
IList<Foo> list = new List<Foo>();
list.Add(new Foo());
DoSomethingWithCopyOfTheList(list);
..
}
public void DoSomethingWithCopyOfTheList(IList<Foo> list)
{
// Do something
}
All parameters are passed by value unless you explicitly use ref or out. However, when you pass an instance of a reference type, you pass the reference by value. I.e. the reference itself is copied, but since it is still pointing to the same instance, you can still modify the instance through this reference. I.e. the instance is not copied. The reference is.
If you want to make a copy of the list itself, List<T> has a handy constructor, that takes an IEnumerable<T>.
You're not alone; this confuses a lot of people.
Here's how I like to think of it.
A variable is a storage location.
A variable can store something of a particular type.
There are two kinds of types: value types and reference types.
The value of a variable of reference type is a reference to an object of that type.
The value of a variable of value type is an object of that type.
A formal parameter is a kind of variable.
There are three kinds of formal parameters: value parameters, ref parameters, and out parameters.
When you use a variable as an argument corresponding to a value parameter, the value of the variable is copied into the storage associated with the formal parameter. If the variable is of value type, then a copy of the value is made. If the variable is of reference type, then a copy of the reference is made, and the two variables now refer to the same object. Either way, a copy of the value of the variable is made.
When you use a variable as an argument corresponding to an out or ref parameter the parameter becomes an alias for the variable. When you say:
void M(ref int x) { ...}
...
int y = 123;
M(ref y);
what you are saying is "x and y now are the same variable". They both refer to the same storage location.
I find that much easier to comprehend than thinking about how the alias is actually implemented -- by passing the managed address of the variable to the formal parameter.
Is that clear?
The list is passed by reference, so if you modify the list in SomeFunction, you modify the list for the caller as well.
You can create a copy of a list by creating a new one:
var newList = new List<Foo>(oldList);
your list is passed by reference. If you want to pass a copy of the list you can do:
IList<Foo> clone = new List<Foo>(list);
if you add/remove elements in clone it won't modify list
but the modifications of the elements themselves will be taken into account in both lists.
When you pass reference type by value (without ref or out keywords) you may modify this reference type inside this method and all changes will reflect to callers code.
To solve your problem you may explicitly create a copy and pass this copy to your function, or you may use:
list.AsReadOnly();
When passing reference types, you pass the reference. This is an important concept.
If you pass a reference
byref, you pass the reference (pointer) directly.
byval, you pass a copy of the reference (pointer).
A reference is not the instance referenced. A reference is analagous to a pointer.
To pass a copy of the instance of a referencetype, you first must make a copy yourself and pass a reference to the copy. As such then you will not be modifying the original instance.
Related
From my understanding passing a variable by reference means that the original variable that was passed into a function's parameters is changed. But also from my understanding a static variable means that the variable is changed when used as a parameter of a function. So don't both of these do the same thing?
From my understanding passing a variable by reference means that the original variable that was passed into a function's parameters is changed.
That location is passed, so yes; since the caller supplied the location, the caller can see the change.
But also from my understanding a static variable means that the variable is changed when used as a parameter of a function.
That's not what that means at all. A static field just exists as a field once per type (or per combination of generic type arguments). That's all it means. When you pass the value of a static field to a method, the value from the static field is read once and copied onto the stack, and that copy is passed to the method. The value of the static field will not be changed during the call.
Perhaps the confusion here is actually "reference types", not "pass by reference"; if a static field is actually a reference to an object, then changes to the object will be observed by all callers. But it isn't the field that changed: it is the object.
I'm having strange issue where I am creating a new object which contains a parameter for another object (paramObj) I use through my function. So, paramObj is used in an object constructor, but it ends up being altered after the constructor is called. Since C# is pass by value, I'm not sure why this is happening.
My code:
void MyFunction(List<string> filesets)
{
foreach(Fileset fs in filesets)
{
//At this point, fs.allFiles.Count is 30. The MyNewObject class
//takes a Fileset as a parameters and eventually clears the
//Fileset.allFiles List, making the count 0.
MyNewObject tmpObj = new MyNewObject(fs, null, "default");
//At this point, fs.allFiles.Count is 0, but I expect it to be 30
}
}
The MyNewObject class simply clears the allFiles list contained within a Fileset class. Why is that showing up after the constructor if C# is pass by value?
You´re right in that everything in .NET is passed by value. Even references - and this is what fs actually is - are passed by value. Thus, when you pass fs around your method will have a copy of that reference. This reference however references the exact same object, making any change on that reference modifiying the backing object also.
So in your constructor you have a second reference to the FileSet-instance referenced by fs.
This more or less leads to the conclusion, that objects are effectivly passed by reference.
There´s no simple way to circumvent this. It depends on why you even modify anything on that object within the constructor at all. You could try to copy the provided object referenced by fs within your constructor, e.g. by implementing IClonable within FileSet or by providing a copy-constructor within that class or whatever. However depending on what FileSet and it´s members are you will need some deep copy of the provided instance.
For further reading on how to make a deep copy of an object look here: Deep cloning objects
Normally, all the objects are passed by reference as parameter to the method. On the other hand most of the primitive data types such as integer, double, Boolean etc. are passed by value.
So I've got a simple string array. Passed it to a function that reverses its input and then displayed the content of the array. I was expecting the contents of the array to reverse since arrays are passed by reference, but the string array did not change.
string[] words = { "Metal", "Gear", "is", "Awesome!" };
mutateArray(ref words);
foreach (string word in words)
Console.Write(word + " ");
This is my mutateArray function:
public static void mutateArray(ref string[] arr)
{
arr = arr.Reverse().ToArray();
}
I know that the mutateArray method changes to the array will persist once I state that the parameter must be passed in with the keyword ref.
Aren't all arrays passed in by reference by default?
Why do the changes persist when the keyword ref is involved?
What's the difference between passing a reference type (classes, interfaces, array, delegates) by value vs passing them by reference (with the keyword ref)?
The reason this doesn't work how you expect is that Reverse() does not actually reverse the contents of the array in place but rather makes a new list with the reversed contents of the original. That's why it works once you pass the array by reference: then, you're actually replacing the entire original array in the calling method with a new one created in mutateArray.
If you had a method that did the reversing in-place, you could pass in the original array (not using ref), and after the method call, the array would be in reverse order.
All parameters are passed by value by default in C#. For reference types like array, this means the reference is passed by value.
ref causes the variable to be passed by reference into the function. This effectively means the arr parameter in mutateArray is an alias for words in the caller. This is why an assignment to arr results in a change in words after mutateArray has exited.
Passing a reference type by value into a function means a copy of the reference is made. Without the ref modifier, arr in mutateArray is a different variable containing a reference to the same object as words in the caller. Assigning to arr in this case has no effect on words in the caller. Note that you can mutate the array through the shared reference, but arr and words are separate storage locations.
You're confusing a Reference Type with passing by Reference. The ref keyword can be applied to both Value and Reference types. Even Reference types aren't passed by reference by default. Instead the reference is passed by value to the method.
Based on the documentation from MSDN, they should. That's the whole purpose of using the ref keyword with a Reference Type.
The difference is that when you pass by Reference Type by Reference, you are able to change the reference of the original variable rather than just the instance inside your method. Check the previously linked documentation for more details.
words is a reference to an array. Just consider it to contain the memory address of that array.
When you give it to MutateArray as a parameter (without the ref keyword) its VALUE will be copied into arr. So arr is a different variable as words, but they contain the same value (= memory address). This means that they refer to the same object (the string array).
You can change the contents of the object, but words (and arr) will still be referring to it.
If you assign arr to a different object, then its value changes, so it will refer to a different object than words.
However, if you use the ref keyword, then arr and words are the SAME variable. That means if you change arr's value (= assign it to a new object), you are also changing words's value, so words will refer to the same, new, object.
Maybe all of this is not technically 100% correct, but it's the way I like to think about it in order to understand how it works.
The ref keyword is why the following Swap method works in C#; without it, it would just change the inner variables of the Swap method (and do nothing basicallly)
public void Swap<T>(ref T a, ref T b) {
T temp = a;
a = b;
b = temp;
}
Your question is totally understandable and for a moment I was confused as well.
So here is the explanation.
Short explanation
When you pass a reference type to a method and make a change to its property, the object outside the method CAN SEE the property change because the object itself remains the same.
When you pass a reference type to a method and make a change to the instance itself, the object outside the method CANNOT SEE the change because inside the method you basically started pointing to another object. So the object changed inside the method and remained there as a stranger.
Long explanation
Suppose you have a reference type instance where you get the value from database.
using (var context = new MyAdventureWorksEntities2())
{
Product p = context.Products.Where(item => item.ProductID == 1000).First();
Console.WriteLine(p.Name); // p.Name = "INITIAL NAME"
UpdateName(p);
Console.WriteLine(p.Name);
}
And here is your UpdateName method:
public static void UpdateName(Product p)
{
p.Name = "UPDATED NAME";
}
This code emits the following result:
INITIAL NAME
UPDATED NAME
HOWEVER, if you change the method to the following:
public static void UpdateName(Product p)
{
using (var context = new MyAdventureWorksEntities2())
{
p = context.Products.Where(item => item.ProductID == 1003).First();
// p.Name = "ANOTHER PRODUCT NAME"
}
}
your result will be:
INITIAL NAME
INITIAL NAME
Note that I didn't touch the ref keyword at all.
And perhaps after those examples the short description will be much more comprehensible.
For some reason, (I am new to C# and know java and c++) C# keeps copying objects when I want to pass by value. I have an arraylist of a Vector2 class, and whenever I want to increment a value, I have to do this:
Vector2 d = (Vector2) myObjects[i];
d.Y++;
myObjects [i] = d;
I want to be able to do this:
Vector2 d = (Vector2) myObjects[i];
d.Y++;
and be done. I searched the web and surprisingly no answers.
BTW the vector is a struct.
In C#, instances of classes are passed as references, whereas instances of structs are passed by copy (by default).
The answer was just where it was supposed to be: http://msdn.microsoft.com/en-us/library/vstudio/ms173109.aspx
A class is a reference type. When an object of the class is created, the variable to which the object is assigned holds only a reference to that memory. When the object reference is assigned to a new variable, the new variable refers to the original object. Changes made through one variable are reflected in the other variable because they both refer to the same data.
A struct is a value type. When a struct is created, the variable to which the struct is assigned holds the struct's actual data. When the struct is assigned to a new variable, it is copied. The new variable and the original variable therefore contain two separate copies of the same data. Changes made to one copy do not affect the other copy.
You are experiencing one of the effects of value-types. Because it copies itself by value, rather than by reference when assigned to new variables or passed as an argument.
You can pass a struct or other value type by ref, using the ref keyword in your method signature, unfortunately you can't use it for treating a variable in the same stack frame as a reference (i.e. you can't just say ref int test = yourArray[0], but must make something like:
public void SomeMethod(ref Vector2 input) {
// now you are modifying the original vector2
}
public void YourOriginalMethod()
{
SomeMethod(yourArray[20]);
}
In response to the comment below, from http://msdn.microsoft.com/en-us/library/14akc2c7.aspx:
Do not confuse the concept of passing by reference with the concept of
reference types. The two concepts are not the same. A method parameter
can be modified by ref regardless of whether it is a value type or a
reference type. There is no boxing of a value type when it is passed
by reference.
Take a look at the following program:
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList(myList);
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList(List<int> myList)
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
I assumed myList would have passed by ref, and the output would
3
4
The list is indeed "passed by ref", but only the sort function takes effect. The following statement myList = myList2; has no effect.
So the output is in fact:
10
50
100
Can you help me explain this behavior? If indeed myList is not passed-by-ref (as it appears from myList = myList2 not taking effect), how does myList.Sort() take effect?
I was assuming even that statement to not take effect and the output to be:
100
50
10
Initially, it can be represented graphically as follow:
Then, the sort is applied myList.Sort();
Finally, when you did: myList' = myList2, you lost the one of the reference but not the original and the collection stayed sorted.
If you use by reference (ref) then myList' and myList will become the same (only one reference).
Note: I use myList' to represent the parameter that you use in ChangeList (because you gave the same name as the original)
You are passing a reference to the list, but your aren't passing the list variable by reference - so when you call ChangeList the value of the variable (i.e. the reference - think "pointer") is copied - and changes to the value of the parameter inside ChangeList aren't seen by TestMethod.
try:
private void ChangeList(ref List<int> myList) {...}
...
ChangeList(ref myList);
This then passes a reference to the local-variable myRef (as declared in TestMethod); now, if you reassign the parameter inside ChangeList you are also reassigning the variable inside TestMethod.
Here is an easy way to understand it
Your List is an object created on heap. The variable myList is a
reference to that object.
In C# you never pass objects, you pass their references by value.
When you access the list object via the passed reference in
ChangeList (while sorting, for example) the original list is changed.
The assignment on the ChangeList method is made to the value of the reference, hence no changes are done to the original list (still on the heap but not referenced on the method variable anymore).
This link will help you in understanding pass by reference in C#.
Basically,when an object of reference type is passed by value to an method, only methods which are available on that object can modify the contents of object.
For example List.sort() method changes List contents but if you assign some other object to same variable, that assignment is local to that method. That is why myList remains unchanged.
If we pass object of reference type by using ref keyword then we can assign some other object to same variable and that changes entire object itself.
(Edit: this is the updated version of the documentation linked above.)
C# just does a shallow copy when it passes by value unless the object in question executes ICloneable (which apparently the List class does not).
What this means is that it copies the List itself, but the references to the objects inside the list remain the same; that is, the pointers continue to reference the same objects as the original List.
If you change the values of the things your new List references, you change the original List also (since it is referencing the same objects). However, you then change what myList references entirely, to a new List, and now only the original List is referencing those integers.
Read the Passing Reference-Type Parameters section from this MSDN article on "Passing Parameters" for more information.
"How do I Clone a Generic List in C#" from StackOverflow talks about how to make a deep copy of a List.
While I agree with what everyone has said above. I have a different take on this code.
Basically you're assigning the new list to the local variable myList not the global.
if you change the signature of ChangeList(List myList) to private void ChangeList() you'll see the output of 3, 4.
Here's my reasoning...
Even though list is passed by reference, think of it as passing a pointer variable by value
When you call ChangeList(myList) you're passing the pointer to (Global)myList. Now this is stored in the (local)myList variable. So now your (local)myList and (global)myList are pointing to the same list.
Now you do a sort => it works because (local)myList is referencing the original (global)myList
Next you create a new list and assign the pointer to that your (local)myList. But as soon as the function exits the (local)myList variable is destroyed.
HTH
class Test
{
List<int> myList = new List<int>();
public void TestMethod()
{
myList.Add(100);
myList.Add(50);
myList.Add(10);
ChangeList();
foreach (int i in myList)
{
Console.WriteLine(i);
}
}
private void ChangeList()
{
myList.Sort();
List<int> myList2 = new List<int>();
myList2.Add(3);
myList2.Add(4);
myList = myList2;
}
}
Use the ref keyword.
Look at the definitive reference here to understand passing parameters.
To be specific, look at this, to understand the behavior of the code.
EDIT: Sort works on the same reference (that is passed by value) and hence the values are ordered. However, assigning a new instance to the parameter won't work because parameter is passed by value, unless you put ref.
Putting ref lets you change the pointer to the reference to a new instance of List in your case. Without ref, you can work on the existing parameter, but can't make it point to something else.
There are two parts of memory allocated for an object of reference type. One in stack and one in heap. The part in stack (aka a pointer) contains reference to the part in heap - where the actual values are stored.
When ref keyword is not use, just a copy of part in stack is created and passed to the method - reference to same part in heap. Therefore if you change something in heap part, those change will stayed. If you change the copied pointer - by assign it to refer to other place in heap - it will not affect to origin pointer outside of the method.