jhon
jhon

Reputation: 121

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

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

Upvotes: 2

Views: 2644

Answers (3)

Ashutosh Raghuwanshi
Ashutosh Raghuwanshi

Reputation: 440

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.

Upvotes: 3

Matt Davis
Matt Davis

Reputation: 46052

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

Upvotes: 1

Joel Coehoorn
Joel Coehoorn

Reputation: 416091

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

Upvotes: 1

Related Questions