Deep C# - Value And Reference
Written by Mike James   
Monday, 04 October 2021
Article Index
Deep C# - Value And Reference
Heap and Stack



It is often said that an important difference between value and reference types is their life cycle. In fact, both types of variable have exactly the same behavior with respect to when they are created and destroyed. That is, a value or a reference type is destroyed as soon as the variable is clearly no longer accessible, i.e. goes out of scope. This means, for example, that a variable defined in a method is destroyed as soon as the function terminates. It is this behavior that makes local variables truly local to the method or block that they were declared in. Notice that there can be exceptions to this rule such as static variables which aren’t destroyed until the application terminates. However, it is true to say that the vast majority of variables do behave in this way.

What is different between value and reference types is what happens to their data when the variable is destroyed. In the case of a value type variable, the variable and its data are one and the same and so when a value type variable is destroyed so is its data. However, a reference type variable only contains a reference to its data and while the variable and the reference it contains is destroyed, the object that it references isn’t.

This is the source of the statement that value and reference variables have different lifetimes. They don’t but the data associated with them can have.

Obviously we can’t leave unwanted objects on the heap forever and this is where the system garbage collector comes in. This is a service that periodically scans the heap looking for objects that no longer have any references to them. An object with no references to it is clearly no longer required and using this fact the garbage collector eventually gets round to clearing up the heap.

Notice that this difference in lifetime is entirely to do with the way that things are stored. The value and reference variables are stored on the stack and this is naturally self-managing in the sense that when a method returns, all of its local variables are destroyed by the adjustment of the stack pointer. Anything stored on the heap has no such natural cleaning process and we have to implement a garbage collection system to determine when they are no longer required and when they should be removed.

How and when to tidy the heap is entirely a matter of efficiency. Garbage collect too often and you use up processor power when the is plenty of heap waiting to be used. Garbage collect too little and you risk bringing the application to a halt while the garbage collector has to work overtime freeing up memory by deleting objects and consolidating free space.



What “value” does a variable have if it hasn’t yet been assigned one?

This is just a question of default values. For numeric types the default value is zero. For strings and other reference types the answer is a little more complicated.

You can assign the default value to any type using:


or, where the compiler can work out the type, just:


The default operator or literal is very useful when used with generics, see Chapter 9. For a simple value type the default is produced by the default constructor and this is the value produced by a bit pattern of all zeros, in other words, zero cast to the appropriate type. For a reference type the default value is the special value null which means that the variable isn’t referencing any object.

Sometimes variables are automatically initialized to their default value and sometimes they aren’t. The rules for automatic initialization of variables seem strange at first. Any local variable in a method is not automatically initialized and if you try and use it you will see a compiler error telling you that you are trying to use an uninitialized variable. However, if the variable is a property of a class then it is initialized, as are the elements of an array. So if you write:

int i;

in a method then you will see a compile-time error message, but if you write;

class MyClassA
	int i;
     	public void myMethod(T x)

everything works as the variable is automatically initialized. The difference comes down to:

variables allocated on the heap are initialized and variables allocated on the stack aren’t.

The compiler is cleverer than you might think in determining if a variable is initialized or not. Consider the following:

Random R = new Random();
int i;
if (R.NextDouble() < 0.5)
    i = 0;

The variable is initialized roughly 50% of time, but the compiler still flags the use of the variable as uninitialized. If you add an else that initializes the variable then everything is fine and the compiler knows that all paths result in an initialized variable. You can spend many a happy hour trying to fool the compiler into thinking that a variable is initialized when it sometimes isn’t!

In Chapter but not in this extract

  • Null
  • Nullable versus NonNullableTypes
  • Nullable Value Types


The difference between stack and heap storage, local and global scope and reference and value types permeates all of computing. Even languages that do their best to hide the distinction are easier to understand when you know about the foundations. The way data is stored and handled explains so much about modern programming and there is unlikely to be a big change in programming languages until these basics are superseded. There is also a sense in which the reference type is the superior type. If everything is an object then every variable should be a reference type.

Deep C#

 Buy Now From Amazon


 Chapter List

  1. Why C#?

    I Strong Typing & Type Safety
  2. Strong Typing
    Why Strong Typing
  3. Value & Reference
  4.    Extract Value And Reference
  5. Structs & Classes
    Structs & Classes 
  6. Inheritance
  7. Interfaces & Multiple Inheritance
    Extract Interface
  8. Controlling Inheritance
    II Casting & Generics
  9. Casting - The Escape From Strong Typing
    Extract Casting I ***NEW!
  10. Generics
  11. Advanced Generics
  12. Anonymous & Dynamic
    III Functions
  13. Delegates
  14. Multicast Delegates
  15. Anonymous Methods, Lambdas & Closures
    IV Async
  16. Threading, Tasks & Locking
  17. The Invoke Pattern
  18. Async Await
  19. The Parallel For
    V Data - LINQ, XML & Regular Expressions
  20. The LINQ Principle
  21. XML
  22. LINQ To XML
  23. Regular Expressions
    VI Unsafe & Interop
  24. Interop
  25. COM
  26. Custom Attributes
  27. Bit Manipulation
  28. Advanced Structs
  29. Pointers 

Extra Material



To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.


The Ongoing State Of JavaScript

The latest State of JavaScript Survey has just been published, bringing us a wealth of details about what parts of the JavaScript ecosystem and used, loved and wanted by developers.

Pgai Brings Your ML Workload To The Database

Extensions like pgai are targeted at the "AI Engineers", a new breed of developers who unlike researchers are concerned with practically applying AI (models, tools, and APIs) to build software.

More News

kotlin book



or email your comment to:


Last Updated ( Monday, 04 October 2021 )