I am wondering, when does it become more efficient to pass by value in C#? I was reading this article:
http://msdn.microsoft.com/en-us/library/4d43ts61%28v=vs.90%29.aspx
The article suggests that passing by reference actually generates overhead when it transferrs the value type to the heap. My C/C++ classes swore by passing by reference as a good coding practice, and I have passed even primitive values by reference for a while now. I am in C# now, so I guess it is possible that it does not funciton the same, but I always thought that passing a pointer to a value was more efficient than copying the value of a variable to a new local variable in memory. Is there a certain number of bytes a data type has to be to become more efficient to pass by reference?
It depends how much data you're copying really. It's nearly always cheaper to pass a class by reference.
Reference types are created on the heap, so there is no penalty of passing a reference type by reference (classes are reference types).
However, to pass a value type (such as a struct) by reference, then you will need to "box" the type and this will allow you to pass by reference.
In essence you will pass value types by reference when;
- You have a large structure
or - You wish to update a value you pass into the method, without returning it as a result.
In option 1, simply boxing, or passing the structure with the ref
keyword (must also be in the method declaration) will be quicker than creating a copy of the data on the stack.
With option 2, it is usually better to use the out
keyword. Where the out
keyword is used, you're specifying that the input value is of no consequence and shouldn't be used, however, you're expecting this value to be filled before the method exits.
My C/C++ classes swore by passing by reference as a good coding practice, and I have passed even primitive values by reference for a while now.
Even in C++ it's a tradeoff. The size of a reference versus the size of the object being passed relative to the cost of an extra level of indirection (even when it's hidden from you). Generally, in both C++ and C#, you can feel confident passing scalar types by value. Scalar types are things like char
, int
, double
, decimal
.
In C# you'll usually be passing by reference except in the odd cases where you're using a value type such as DateTime.
Even then, my recommendation is not to worry about it for performance reasons unless you have profiling results that show a slowdown, you've done everything you can at a higher level, and micromanaging how you pass parameters is the next step.
Instead, focus on functionality. If you want to update the object being passed either by reseating the reference or creating a reference from a value type so that it can be modified from another function, consider using either a ref
or out
parameter. Otherwise, use the default passing semantics for the type.
The trick is to do what you need to do when you need to do it and not before. Otherwise you'll end up with over-engineered code that's harder to maintain and may even be counterproductive in terms of performance.
So I have read what you both posted, and they had some very good points. I also asked my C# teacher more about pass by reference, and I had some of the topic confused. So pass by reference basically boxes, or stores a pointer to the data type provided in the heap. This address in the heap is passed to the method. These pointers used to be about a ubyte in size, and probably still are according to my teacher. So given what my teacher told me, it stands to reason that it is more efficient to pass by reference for any value larger than a ubyte. Given this, it would be more efficient in all situations to pass by reference rather than copy the whole value in a pass by value operation. The article that I gave you all to look at really did me a disservice though, because it suggested that there is overhead in passing by reference. There is some overhead yes, but no more than is necessary and compared to pass by value it is much better, so it should not be portrayed as such. I suppose what I am trying to say is that coding it's self generates overhead, pass by reference is much better in terms of overhead than pass by value.
Thank you all, the universe has righted it's self again!
Hmm, no. Your teacher is incorrect. C# can be compiled as either 32bit or 64bit. Meaning your memory pointers are either 4 bytes or 8 bytes. This was the same in C++ if I recall correctly. (Deceptikon?)
The pointer object in C# is called IntPtr
which really gives it away ;)
Additionally, byte
in C# is already unsigned. The signed version is char
.
Also, classes (references types) are not boxed. They are simply passed by reference because they already exist on the heap. If it helps, imagine every class definition as Class*
.
With the built in primitives (int
, long
, ulong
etc), it's almost always cheaper and easier to pass by value.
It becomes a case of mathematics when you start defining structs
. Structs are value types, even though they contain multiple values within them. This is also kind of important. When you copy a struct, you copy all the members of that struct, apart from the reference types, which are still referenced.
This is important...
using System;
class MyTestClass
{
public int TestInt { get; set; }
}
struct MyTestStruct
{
public MyTestClass TestClass { get; set; }
public int TestInt { get; set; }
}
class MainEntryPoint
{
static void Main()
{
MyTestStruct testStruct = new MyTestStruct();
testStruct.TestClass = new MyTestClass();
testStruct.TestClass.TestInt = 5;
testStruct.TestInt = 5;
UpdateStruct(testStruct);
Console.WriteLine("TestClass Int: {0}", testStruct.TestClass.TestInt);
Console.WriteLine("TestStruct Int: {0}", testStruct.TestInt);
Console.ReadLine();
}
static void UpdateStruct(MyTestStruct testStruct)
{
testStruct.TestClass.TestInt = 3;
testStruct.TestInt = 10;
}
}
The output is:
TestClass Int: 3
TestStruct Int: 5
Even though the struct was passed by value, the class within it was copied by reference.
So pass by reference basically boxes, or stores a pointer to the data type provided in the heap.
No. Passing by reference doesn't box a value type. Let's also differentiate between passing by reference and passing an object reference:
void foo(object obj); // Passing an object reference
void foo(ref object obj); // Passing by reference
When passing an object reference, the reference to an object is passed by value. When a value type is passed by reference, compiler magic allows the called method to access the original entity.
This address in the heap is passed to the method.
Not necessarily. Deep under the hood you may ultimately have a pointer (another object holding an address), but it's equally likely that the compiler is smart enough to simply bind another symbol in the symbol table to the same address and avoid any kind of indirection.
However, if a pointer is used, you introduce the overhead of fetching the object at that address. This can overwhelm any savings you might get from the performance of minimizing copying in the method call.
Given this, it would be more efficient in all situations to pass by reference rather than copy the whole value in a pass by value operation.
Would it? Try writing a test where you call a method with a value parameter in a long loop and profile it. Then do the same for a long loop that calls a method with a ref parameter.
I'd wager you'll find that the difference (if any) is so minute that efficiency of passing by reference or passing by value shouldn't even be on your radar except for very large value types.
Once again, code for clarity and robustness first, then optimize when you can empirically prove that there's a performance problem. Start optimizing at the highest level, such as picking better algorithms, and only micro-optimize as a last resort.
I wrote my own program to test this out, and you were right, if one want's to save processing cycles, this is not the place to do it with primitive types. Arrays, classes, and structs are the only place where this makes sense. When using my program I noticed that passing by reference and passing by value would frequently change places in terms of being the fastest. It would be interesting to be able to put the information gathered from a program like this into a function curve in a graphing program. Here is the code I wrote, tell me if there are any flaws. Also I noticed that the values are very close together, which tells me that the difference between the two is negligable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace Test
{
class Program
{
static void Main(string[] args)
{
int numTries = 1000;
passingComparisonInt(numTries);
pause();
}
private static void passingComparisonInt(int numTries)
{
double[] times1 = testByRefInt(numTries);
double avg = average(times1, numTries);
Console.WriteLine("The average time for a pass by reference is: {0}", avg);
double[] times2 = testByValInt(numTries);
avg = average(times2, numTries);
Console.WriteLine("The average time for a pass by value is: {0}", avg);
}
private static double[] testByRefInt(int numTries)
{
int x = 0;
Stopwatch sw = new Stopwatch();
double[] times = new double[numTries];
for (int i = 0; i < numTries; i++)
{
sw.Start();
myMethod1(ref x);
sw.Stop();
times[i] = sw.Elapsed.TotalMilliseconds;
}
return times;
}
private static double[] testByValInt(int numTries)
{
int x = 0;
Stopwatch sw = new Stopwatch();
double[] times = new double[numTries];
for (int i = 0; i < numTries; i++)
{
sw.Start();
myMethod2(x);
sw.Stop();
times[i] = sw.Elapsed.TotalMilliseconds;
}
return times;
}
private static double average(double[] times, int numTries)
{
double average = 0;
for (int i = 0; i < times.Length; i++)
average += times[i];
average /= numTries;
return average;
}
public static void myMethod1(ref int x) { }
public static void myMethod2(int x) { }
public static void pause()
{
Console.Write("Press any key to continue... ");
Console.ReadLine();
}
}//end class
}//end namespace
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.