Parameter Passing in C#

Preamble

Many people have become fairly confused about how parameters are passed in C#, particularly with regard to reference types. This page should help to clear up some of that confusion. If you have any suggestions for how it can be made clearer, please mail me.

Microsoft also has a good page about this topic (which I believe uses exactly the same terminology as this page - let me know if they appear to disagree).

Preamble: what is a reference type?

In .NET (and therefore C#) there are two main sorts of type: reference types and value types. They act differently, and a lot of confusion about parameter passing is really down to people not properly understanding the difference between them. Here's a quick explanation:

A reference type is a type which has as its value a reference to the appropriate data rather than the data itself. For instance, consider the following code:

StringBuilder sb = new StringBuilder();

(I have used StringBuilder as a random example of a reference type - there's nothing special about it.) Here, we declare a variable sb, create a new StringBuilder object, and assign to sb a reference to the object. The value of sb is not the object itself, it's the reference. Assignment involving reference types is simple - the value which is assigned is the value of the expression/variable - i.e. the reference. This is demonstrated further in this example:

StringBuilder first = new StringBuilder();
StringBuilder second = first;

Here we declare a variable first, create a new StringBuilder object, and assign to first a reference to the object. We then assign to second the value of first. This means that they both refer to the same object. They are still, however, independent variables themselves. Changing the value of first will not change the value of second - although while their values are still references to the same object, any changes made to the object through the first variable will be visible through the second variable. Here's a demonstration of that:

StringBuilder first = new StringBuilder();
StringBuilder second = first;
first.Append ("hello");
first = null;
Console.WriteLine (second);

Output:

Here, we declare a variable first, create a new StringBuilder object, and assign to first a reference to the object. We then assign to second the value of first. We then call the Append method on this object via the reference held in the first variable. After this, we set the first variable to null (a value which doesn't refer to any object). Finally, we print out the results of calling the ToString method on the StringBuilder object via the reference held in the second variable. hello is displayed, demonstrating that even though the value of first has changed, the data within the object it used to refer to hasn't - and second still refers to that object.

Class types, interface types, delegate types and array types are all reference types.

Further preamble: what is a value type?

While reference types have a layer of indirection between the variable and the real data, value types don't. Variables of a value type directly contain the data. Assignment of a value type involves the actual data being copied. Take a simple struct, for example:

public struct IntHolder
{
    public int i;
}

Wherever there is a variable of type IntHolder, the value of that variable contains all the data - in this case, the single integer value. An assignment copies the value, as demonstrated here:

IntHolder first = new IntHolder();
first.i=5;
IntHolder second = first;
first.i=6;
Console.WriteLine (second.i);

Output:

Here, second.i has the value 5, because that's the value first.i has when the assignment second=first occurs - the values in second are independent of the values in first apart from when the assignment takes place.

Simple types (such as float, int, char), enum types and struct types are all value types.

Note that many types (such as string appear in some ways to be value types, but in fact are reference types. These are known as immutable types. This means that once an instance has been constructed, it can't be changed. This allows a reference type to act similarly to a value type in some ways - in particular, if you hold a reference to an immutable object, you can feel comfortable in returning it from a method or passing it to another method, safe in the knowledge that it won't be changed behind your back. This is why, for instance, the string.Replace doesn't change the string it is called on, but returns a new instance with the new string data in - if the original string were changed, any other variables holding a reference to the string would see the change, which is very rarely what is desired.

Constrast this with a mutable (changeable) type such as ArrayList - if a method returns the ArrayList reference stored in an instance variable, the calling code could then add items to the list without the instance having any say about it, which is usually a problem. Having said that immutable reference types act like value types, they are not value types, and shouldn't be thought of as actually being value types.

For more information about value types, reference types, and where the data for each is stored in memory, please see my other article about the subject.

Checking you understand the preamble...

What would you expect to see from the code above if the declaration of the IntHolder type was as a class instead of a struct? If you don't understand why the output would be 6, please re-read both preambles and mail me if it's still not clear - if you don't get it, it's my fault, not yours, and I need to improve this page. If you do understand it, parameter passing becomes very easy to understand - read on.

You might also like...

Comments

About the author

Jon Skeet United Kingdom

C# MVP currently living in Reading and working for Google.

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the Universe trying to produce bigger and better idiots. So far, the Universe is winning.” - Rich Cook