While the CLR stores the data for value types on the stack, the CLR stores only a pointer to a reference type’s data on the stack. The stack is a frequently used location in memory that is only used to store small pieces of data. A pointer is an address in memory. The pointer indicates where the reference type’s data is stored.
As you might have noticed, all the value types are quite simple, consisting mostly of numbers that consume only a few bytes of memory. Because value types are so simple, their entire data can be stored on the stack. Reference types can be very large, consisting of megabytes of data or even more. It would be inefficient to store so much data on the stack. Therefore, the CLR stores data for a reference type in a different memory location known as the heap.
This page will show you how to:
- List common reference types
- Describe how value and reference types are each passed to methods
- Anticipate the behavior of code that updates a type after it is passed to a method
Built-in Reference Types
The .NET Framework includes more than 2,500 built-in reference types. This table lists the most commonly used typed. Each of these types supports the ToString, GetType, and Equals methods.
|System.Object||The most general type in the .NET Framework. Any type can be cast to (and from) Object, which is useful when a method needs to accept multiple types–just create the method to accept an Object parameter, and then cast it to the appropriate type.|
|System.StringBuilder||Text data that will be updated multiple times. Use StringBuilder instead of String for better (if not noticeably better) performance when concatenating (adding) several strings into each other.|
|System.Array||Arrays of data. This is the base class for all arrays.|
|System.IO.Stream||Reading and writing files. Often, you will use classes that are derived from Stream, such as TextReader and TextWriter.|
|System.Exception||Handling system and application exceptions. The .NET Framework provides many classes derived from the base Exception class to provide detailed problem-specific information. Additionally, you can create your own custom Exception classes to describe problems that might occur in your application.|
Passing Types By Reference
When you pass an instance of a value type to a method, the .NET Framework copies the object. This prevents the method from making changes to the original object. If you want the method to make changes, you can pass the object by reference using the ByRref keyword in Visual Basic.
The following code sample is identical to the similar code sample shown in the Value Types lesson, except that the integer is passed by reference. As a result, the application displays 4, rather than 2.
Private Shared Sub Main(ByVal args As String()) Dim i As Integer = 2 AddTwo(i) Console.WriteLine(i) End Sub Private Shared Sub AddTwo(ByRef n As Integer) n += 2 End Sub
Passing Values and References (Video)
Watch my video explaining this in more detail, with the transcript below:
Here’s a quick animation to illustrate the difference between passing objects by value and passing objects by reference. By default, the .NET Framework passes objects by value.
So, imagine that you wanted to give a note to a friend. When you pass an object by value, which is the default, you actually make a copy of that note and give that to the friend. That way, if the friend makes a change to it, you never see those changes. They can do whatever they want to it, and it will never effect the original value.
In the context of the .NET Framework, that means that any modifications made to the object by the method you call are going to be lost when the method ends.
Now, consider passing an object by reference. In Visual Basic, you need to add the ByRef keyword, and in C#, you need to add the ref keyword.
Notice that there’s no keywords necessary when passing an object by value. In Visual Basic, you can use the ByVal keyword to pass an object by value exlicitly, but it’s the default anyway. There’s not even a keyword in C#.
So, if you pass an object by reference, you actually give that object to the recipient. Any changes they make will be returned to the calling method, even after the method has ended.
In the context of the .NET Framework, the object isn’t actually moved. Instead, a reference to the object is passed. But, any changes made to the object do impact the original object.
Boxing and Unboxing
The .NET Common Language Runtime (CLR) can automatically convert value types to reference types. This process is called boxing. Similarly, the CLR can convert reference types to value types using a process known as unboxing.
For example, copying an integer (a value type) to an object (a reference type) performs boxing, as this code sample demonstrates.
Dim i As Integer = 42 ' i is a value type Dim o As Object = i ' i is boxed into o Dim n As Integer = CInt(o) ' o is unboxed into n
Note: any time you convert a type from one class to another, even if there is no boxing or unboxing, it’s known as casting.
The CLR automatically handles boxing and unboxing. However, because boxing can be time-consuming, if you need to pass a value type to a method that might repeatedly perform boxing, you might want to manually box a value type to pass the value type as a reference.
When declaring a method, you can use the ByRef keyword in Visual Basic or the ref keyword in C# to pass a value type by reference. The following code sample demonstrates how to declare a method that receives a value type passed as a value type and a method that receives a value type passed by reference.
Private Function MethodByValue(ByVal number As Integer) As Integer ' parameter is passed by value End Function Private Sub MethodByReference(ByRef number As Integer) ' parameter is passed by reference End Sub