In C# what is the benefit of passing value by ref? - c#

I am trying to understand what is the benefit of passing method params by ref in c# instead of passing parameters by value.
I have a list of custom objects (around 50k) and I need to run some operation on its properties. I have written a calculation class which accepts that list of 50K elements and returns the value. I am wondering if I pass the parameter by the ref, is it going to save my system memory in runtime as I am passing the reference and not passing a copy of the 50k list? How does .NET maintains this actually?
main(){
var itemList={}//list containing 50k items
var result=Calculate(itemList);// it passes a copy of the array
var resultByRef=Calculate(ref itemList); //**it passes address of result variable, is it going to take less memory in runtime??**
}
private int Calculate(List<CustomClass> itemList){
//do some calculation
return result;
}
private int CalculateByRef(ref List<CustomClass> itemList){
//do some calculation
return result;
}

Looks like you are coming from C++ background like me.
In C# every object is passed around but its reference all the time which means no matter how large the object is, you always pass its reference to methods.
The only difference the ref keyword makes is give you ability to change that reference itself. Let's understand with an example:
static void callByRef(ref byte[] buff)
{
buff[0] = 10;
buff = new byte[5];
}
static void callNormally(byte[] buff)
{
buff[0] = 10;
buff = new byte[5];
}
static void Main(string[] args)
{
byte[] param = new byte[5];
param[0] = 5;
Console.WriteLine("Original param.Hash: " + param.GetHashCode());
callNormally(param);
Console.WriteLine("param[0]: " + param[0]);
Console.WriteLine("param.Hash: " + param.GetHashCode());
callByRef(ref param);
Console.WriteLine("param[0]: " + param[0]);
Console.WriteLine("param.Hash: " + param.GetHashCode());
return;
}
The output is as follows:
Origenal param.Hash: 58225482
param[0]: 10
param.Hash: 58225482
param[0]: 0
param.Hash: 54267293
In a normal call too, you can change the contents inside the object but in case of ref call the object itself can be changed.
In your case you are only worried about memory replication in case of passing large data as parameter to a method which happens in case of C++. In C# that is not the case.

Passing by reference will not help you here.
This is because passing by value for a reference type list a list or array still passes the reference. The difference is when you pass by value, you pass a copy (value) of the reference, but it's only the reference that is copied. When you pass by reference you pass the original variable.
Copying a mere 20 byte reference isn't meaningfully different than what you need to do to make actual reference passing work, and so there's no performance advantage. Passing by reference is only useful if you need to change the variable itself: for example, assign a completely new List object to it.
//pass itemList by value
private int Calculate(List<CustomClass> itemList)
{
itemList[0] = new CustomClass(); // this still works!
//The List reference that was passed to this method still refers
// to the *same List object* in memory, and therefore if we update
// the item at position [0] here it will still be changed after the
// method returns.
// But this does NOT change the original after the method ends,
// because itemList is a different variable and we just changed
// it to refer to a whole new object.
itemList = new List<CustomClass>();
// If we had instead passed this by reference, then the calling code
// would also see the brand new list.
}

Let's work through the following example.
static void Main(string[] args)
{
var myLocalList = new List<int> { 1, 2, 3 }; // 1
myLocalList.ForEach(x => Console.WriteLine(x)); // 2
Calculate1(myLocalList); // 3
myLocalList.ForEach(x => Console.WriteLine(x)); // 5
Calculate2(ref myLocalList); // 6
myLocalList.ForEach(x => Console.WriteLine(x)); // 8
}
private void Calculate1(List<int> list)
{
list = new List<int> { 4, 5, 6 }; // 4
}
private void Calculate2(ref List<int> list)
{
list = new List<int> { 7, 8, 9 }; // 7
}
Step 1 creates a local list of integers initialized with values 1, 2, and 3.
Step 2 prints the list. The console output shows 1, 2, and 3 on separate lines.
Step 3 calls Calculate1 with the local list as a parameter input.
Step 4 assigns the list variable a new list of integers with values 4, 5, and 6.
Step 5 prints the list. The console output shows 1, 2, and 3 on separate lines, same as Step 2.
Step 6 calls Calculate2 with the local list as a ref parameter input.
Step 7 assigns the list variable a new list of integers with values 7, 8, and 9.
Step 8 prints the list. This time, the console output shows 7, 8, and 9 on separate lines.
When myLocalList is passed to Calculate1, the list is not copied. To be absolutely clear, what I mean specifically is that the contents of myLocalList are NOT copied to the list parameter. What is copied, however, is the reference to myLocalList. In other words, the reference to myLocalList is copied by value to the list parameter. When step 4 sets list to the new 4-5-6 list, the copied reference (i.e., list) is modified, not the original reference (i.e. myLocalList).
That changes with Calculate2. In this case, the reference to myLocalList is passed by reference to the list parameter. This effectively turns list into an alias for myLocalList, meaning that when step 7 sets list to the new 7-8-9 list, the original reference (i.e., myLocalList) is modified. That's why the output changes in step 8.
...is it going to save my system memory in runtime as I am passing the
reference and not passing a copy of the 50k list?
No. Neither the Calculate nor the CalculateByRef methods receive deep copies of itemList, so performance is not impacted in the way you suggest. Passing the parameter using the ref keyword in CalculateByRef simply allows you to modify the value of the itemList variable in Main from inside CalculateByRef.
Just based on what you've shown, it doesn't sound like you need the ref keyword in this case.
HTH

Related

C# array parameter reference [duplicate]

This question already has answers here:
Passing Objects By Reference or Value in C#
(9 answers)
Closed 5 years ago.
I have a C# code as follows:
int[] A = new int[] {1, 2, 3};
fun(A);
// A at this point still says 1, 2, 3.
void fun(int[] A)
{
int[] B = new int[] {4, 5, 6};
A = B;
}
I thought all arrays are passed by reference in C#. Shouldn't A after calling fun() reflect 4, 5, 6?
The array is passed by a reference, you can see this by doing A[0] = 7; from inside another method.
That reference (held by the outer variable A), however is passed by value to the function. The reference is copied and a new variable is created and passed to the function. The variable outside the function is not affected by the reassignment to the parameter variable A inside the function.
To update the original variable you need to use the ref keyword so the parameter inside the function represents the same object as outside of the function.
int[] A = new int[] {1, 2, 3};
fun2(A);
// A at this point says 7, 2, 3.
fun(ref A);
// A at this point says 4, 5, 6.
void fun2(int[] a)
{
a[0] = 7;
}
void fun(ref int[] a)
{
int[] B = new int[] {4, 5, 6};
a = B;
}
I thought all arrays are passed by reference in C#
Actually (the reference of the original array object is passed by value) which is the usual behavior in case of reference types in C#.
Your understanding is partially correct, the reference is passed but is passed by value which means a new reference gets created which is pointing to the original array object A.
The fun(int[] A) has it's own copy of reference which is pointing to the array object which contains 1,2,3 and in the fun you create a new array object B and you are just assigning the reference of new one to your local method reference variable which of-course will not have any impact on the original A object which was passed as input to the fun.
You would need to pass it by reference if you want to reflect the changes made to A in fun to be reflected back to the original array object.
You can update the array items without passing by reference which is explained well in Scott Chamberlain's answer
Hope it Helps!

How does the keyword `ref` affect memory management and garbage collection?

I am new to C# and I have been messing around with 'ref', 'out' and pointers, and I have a general question about how 'ref' works, especially when using objects and not primitive types. Say this is my method:
public void foo(ref Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
and a similar method:
public void bar(Point p) {
p.set(1,1); // the x/y values are updated without constructing a new Point
}
EDIT: Point is a class in both cases
Both work, but is one more cost effective than the other? I know in C++ if you pass in a pointer you are only giving the memory address; from my understanding of C#, you cannot pass in an Object* into a method because of the automatic garbage collection. Does 'ref' pin an object to a location? Also, if you pass in an object to a method, like 'bar' above, is it passing a copy of the object or is it passing a pointer/reference?
Clarification: In my book I have, it does say if you want a method to update a primitive, such as int, you need to use ref (out if it is not initialized) or a *. I was asking if the same holds true for objects, and if passing an object as a parameter rather than a ref to an object costs more.
If your type is a struct, ref is roughly equivalent to a pointer to that struct. No new instances are created here. The difference (from passing it without ref) is that you can now mutate the original struct instance contained in that variable.
If your type is a class, ref simply adds one more level of indirection. No new instances are created here either. The difference (from passing it without ref) is that you can now entirely replace (not just mutate) the original class instance referenced by that variable with something else.
Since no new instances are created in either case, the garbage collector probably won't care about this in any important way.
In fact, class is a reference type, it mean that a variable of a reference type hold a reference to it's data instead of holding is data directly like value type.
When you pass a variable of a reference type as method parameter, it pass the reference to that data, not the data itself. So if update some properties of your object, the update is reflected in the original variable, except if you reassign the parameter.
Example from MSDN :
class PassingRefByVal
{
static void Change(int[] pArray)
{
pArray[0] = 888; // This change affects the original element.
pArray = new int[5] {-3, -1, -2, -3, -4}; // This change is local.
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr [0]);
Change(arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr [0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: 888
*/
Passing a variable of a reference type with the ref keyword will reflect any change to the original variable, even if you reassin the parameter.
Example from MSDN :
class PassingRefByRef
{
static void Change(ref int[] pArray)
{
// Both of the following changes will affect the original variables:
pArray[0] = 888;
pArray = new int[5] {-3, -1, -2, -3, -4};
System.Console.WriteLine("Inside the method, the first element is: {0}", pArray[0]);
}
static void Main()
{
int[] arr = {1, 4, 5};
System.Console.WriteLine("Inside Main, before calling the method, the first element is: {0}", arr[0]);
Change(ref arr);
System.Console.WriteLine("Inside Main, after calling the method, the first element is: {0}", arr[0]);
}
}
/* Output:
Inside Main, before calling the method, the first element is: 1
Inside the method, the first element is: -3
Inside Main, after calling the method, the first element is: -3
*/
MSDN documentation.
Quick distinction between a class vs. a struct:
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.
https://msdn.microsoft.com/en-us/library/ms173109.aspx
Your example is tricky because in c# Point is an immutable struct, not an object.
Hopefully this example will help show what happens with structs and objects with and without ref.
public static void StructTest()
{
var fooStruct = new MyStruct();
var barStruct = new MyStruct();
Console.WriteLine(fooStruct.Value); // prints 0
Console.WriteLine(barStruct.Value); // prints 0
fooStruct(ref fooStruct);
barStruct(barStruct);
// Struct value only changes when passed by reference.
Console.WriteLine(fooStruct.Value); // prints 1
Console.WriteLine(barStruct.Value); // prints 0
}
public void fooStruct(ref MyStruct m)
{
m.Value++;
}
public void barStruct(MyStruct m)
{
m.Value++;
}
public static void ObjectTest()
{
var fooObject = new MyObject();
var barObject = new MyObject();
Console.WriteLine(fooObject.Value); // prints 0
Console.WriteLine(barObject.Value); // prints 0
fooObject(ref fooObject);
barObject(barObject);
// Objects are automatically passed by reference. No difference.
Console.WriteLine(fooObject.Value); // prints 1
Console.WriteLine(barObject.Value); // prints 1
fooSetObjectToNull(ref fooObject);
barSetObjectToNull(barObject);
// Reference is actually a pointer to the variable that holds a reference to the object.
Console.WriteLine(fooObject == null); // prints true
Console.WriteLine(barObject == null); // prints false
}
public void fooObject(ref MyObject m)
{
m.Value++;
}
public void barObject(ref MyObject m)
{
m.Value++;
}
public void fooSetObjectToNull(ref MyObject m)
{
m = null;
}
public void barSetObjectToNull(MyObject m)
{
m = null;
}

What is the difference between these two methods - pass by ref

What is the difference between the code below? Both methods achieve the same output of removing 4 from the list.
static void Main(string[] args)
{
var list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9};
ModifyList(ref list);
Console.WriteLine(string.Join(", ", list));
list.Add(4);
ModifyList(list);
Console.WriteLine(string.Join(", ", list));
Console.ReadLine();
}
public static void ModifyList(ref List<int> list)
{
list.Remove(4);
}
public static void ModifyList(List<int> list)
{
list.Remove(4);
}
In this case, both do exactly the same thing. The difference between passing the argument in by ref is that if you assign to the variable itself (eg. list = new List<int>();, it will update the reference of the caller to point to the new list as well.
See Passing Reference-Type Parameters (C# Programming Guide) for more info.
In this case the only difference is under the hood and not easily noticeable, the difference is that in the ModifyList(ref List<int> list) the list object is passed by reference and so the original reference (managed pointer) is the same as the original object passed, while in the second case the reference is copied to another reference so the list argument is basically a reference to another reference that point to the real object .
The result you get is the same, because in C# with safe reference is totally transparent to you but if you would have used C or C++ with raw pointers you would have noticed that in the second case you would had to dereference the pointer two times to access the object ...

Pointer to a dictionary<> in c#

I have a class, which its constructor is below
public GroupsForm(ref Dictionary<string, List<string>> groupsList)
I want to use the groupsList in other functions as well so I declared a private member in the class:
private Dictionary<string, List<string>> _groupsList;
But the problem is when I do _groupsList = groupsList; it makes a copy of the groupsList and changes made to _groupsList doesn't change groupsList, as I think it makes a deep copy by default. What I want is that it should point to that list.
In C++, this can easily be done via pointers, but can anyone tell me how can I do this in C#?
Ahmed has posted this image:
There are value types and reference types in C#. A dictionary is a reference type so whenever assigning a variable that holds a reference type to another, the reference is copied, which means that both variables will be holding the same data. So, if you change the data, you should expect it to change on both variables:
List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = list1;
list2.Add(10);
list1.ForEach(x => Console.WriteLine(x)); // should print 1, 2, 3 and 10.
However, if you reassign the variable, then you are not changing the data:
List<int> list1 = new List<int>() { 1, 2, 3 };
List<int> list2 = list1;
list2 = new List<int>() { 10, 11, 12 };
list1.ForEach(x => Console.WriteLine(x)); // should print 1, 2, 3 only
One thing that caught my attention in your code though was that the constructor is expecting a ref. This means that you are passing the reference itself by reference, which in C++ would be something like a double pointer (type**). You'd do this if, in the snippet above, you want to have this effect:
void MethodA(ref List<int> list)
{
list = new List<int>() { 10, 11, 12 };
}
// ...
List<int> list1 = new List<int>() { 1, 2, 3 };
MethodA(ref list1);
list1.ForEach(x => Console.WriteLine(x)); // should print 10, 11, 12
One more thing -- AFAIK C# doesn't implement deep copies in any of it classes. You have to do it yourself.
In your InitGroups method, you've got an assignment statement to _groupsList - it's this new list that contains 3 items. You could change InitGroups to do something like:
var newGroups = (Dictionary<string, List<string>>)ser.ReadObject(reader,true);
foreach(var kvp in newGroups)
{
_groupsList.Add(kvp.Key,kvp.Value);
}
And then you'll still be working with the same Dictionary object.
You don't need the ref in your method signature. Objects are passed by reference anyway.
And no, there is no copying going on.
But the problem is when I do _groupsList = groupsList; it makes a copy of the groupsList
No, it does not. It copies a pointer. List is a reference type.
as I think it makes a deep copy by default
Think again. Your conclusion is wrong.
List<T> is a reference type, so all variables are pointers. Beginner C#, first week, first day, first hour. Go back and read the introduction then look for the error in the rest of your code you do not show us.
You are wrong on several points.
C# references are totally different from C++.
In short, you should not use ref here because it's for propagating assignments made to parameter to the calling code which is not what you mean.
You are wrong assuming there's any kind of copying involved here.
groupsList and _groups ist are two references pointing to the same object in memory.
You'll need to read more about reference and value types in C#.

C# Calling a Method, and variable scope

Why is cards being changed below? Got me puzzled.. understand passing by ref which works ok.. but when passing an Array is doesn't do as I expect. Compiling under .NET3.5SP1
Many thanks
void btnCalculate_Click(object sender, EventArgs e)
{
string[] cards = new string[3];
cards[0] = "old0";
cards[1] = "old1";
cards[2] = "old2";
int betResult = 5;
int position = 5;
clsRules myRules = new clsRules();
myRules.DealHand(cards, betResult, ref position); // why is this changing cards!
for (int i = 0; i < 3; i++)
textBox1.Text += cards[i] + "\r\n"; // these are all new[i] .. not expected!
textBox1.Text += "betresult " + betResult.ToString() + "\r\n"; // this is 5 as expected
textBox1.Text += "position " + position.ToString() + "\r\n"; // this is 6 as expected
}
public class clsRules
{
public void DealHand(string[] cardsInternal, int betResultInternal, ref int position1Internal)
{
cardsInternal[0] = "new0";
cardsInternal[1] = "new1";
cardsInternal[2] = "new2";
betResultInternal = 6;
position1Internal = 6;
}
}
Arrays are reference types which in short means the value of the array is not directly contained within a variable. Instead the variable refers to the value. Hopefully the following code will explain this a bit better (List<T> is also a reference type).
List<int> first = new List<int>()( new int[] {1,2,3});
List<int> second = first;
first.Clear();
Console.WriteLine(second.Count); // Prints 0
In this scenario there is a List<int> created on the first line which is referred to by variable first. The second line does not create a new list but instead creates a second variable named second which refers to the same List<int> object as first. This logic applies to all reference types.
When you pass the variable cards into the method you do not pass a copy of the full array but instead a copy of the variable cards. This copy refers to the same array object as the original cards. Hence any modifications you make to the array are visible through the original reference.
A variable of a reference type does
not contain its data directly; it
contains a reference to its data. When
you pass a reference-type parameter by
value, it is possible to change the
data pointed to by the reference, such
as the value of a class member.
However, you cannot change the value
of the reference itself; that is, you
cannot use the same reference to
allocate memory for a new class and
have it persist outside the block. To
do that, pass the parameter using the
ref or out keyword.
http://msdn.microsoft.com/en-us/library/s6938f28(VS.80).aspx
When you are passing a reference type (like an array) to a method by value, you are passing a copy of it's reference. It's still the same object that is referenced, it doesn't create a copy of the array itself.
When passing parameters to methods, there are three different concepts to be aware of:
By Value vs By Reference parameters
Value vs Reference types
Mutable vs Immutable types
In your example, the string array is a Reference type, is a Mutable type, and is passed By Value. The compiler will always let you change the content of the array because it is Mutable. However, since it is a Reference type, the calling code and the called code both point to the same array contents, so the calling code "sees the changes". The fact that it's passed by value in this case is irrelevant, since although the called code's array variable has indeed been passed a copy of the calling code's variable, they both point to the same location in memory.
As other answers have said, it's because a reference is being passed by value.
I have an article on argument passing in C# which you may find useful, in addition to the answers here.
Arrays are reference types, thus are subject to change.
When you are passing an array as an object it is not copied. The receiving method works with the same instance. In a sense arrays are always passed by ref. When an array as well as an instance of any other reference type is passed as a parameter the receiving method gets its own copy of a reference on the same instance of the type. No copy of the actual object is created.
If you need to pass a copy you have to be explicit about this: create a copy yourself or clone the array. The reason it is not done for you is obvious - copying an array can be expensive, you do not want it unless it is really necessary

Categories