The .Net type system is indeed an interesting topic, it is common place for interviewers to ask questions on this topic, and in most cases alot of engineers even experienced engineers misunderstand this topic.
Proper understanding on this topic, would infact help you build better efficient and performant applications.
So, back to the topic, in .Net the type system is majorly divided into two, Reference Types and Value Types. If you ask alot of engineers what the difference is between the two, you might here, reference types are allocated on the heap, while value types are allocated on the stack.
But is this really true ?
Well, Yes and No... Let me explain.
In .Net everything is passed as a value, including reference types, they are passed as a value when passed as an argument to a method. What does this mean ?, let me explain with an example.
Say we have a method Main
that declares two variables
public void Main()
{
var a = 5;
Increment(a);
var b = new Car("ford");
Change(b);
}
public void Increment(int val)
{
val = 10;
}
public void Change(Car car)
{
car = new Car("mercedes");
}
After the execution of the Main
method what are the results ?, indeed because a
is a value type, nothing new, the value dosen't change, despite it being changed in the called method. But something amazing happening here is the value of b
despite it being a reference type the value still dosen't change.
So what does this mean ? yes indeed everything is copied by value in .Net, both reference types and value types.
But what is this value we are talking about ?. The answer lies inside what exactly a variable holds. In .Net when you declare a variable, the value of the variable is actually stored on the stack, for value types the value stored is actually the value it points to, for example, var a = 5
the value stored on the stack is 5, for reference types, it gets interesting, the new
keyword actually allocates memory on the managed heap, and returns the address, and this is what is stored on the stack.
That was why when we passed the reference type and changed it, nothing happened to the original variable, it is because the value on the stack(address to the heap) is what was indeed copied.
But if we changed the example a little , and replaced a value within the car
, say we change the Name
property in the Change
method, you would notice that indeed the Car
in main has changed. As what changes here is the value in the managed heap.
public void Main()
{
var a = 5;
Increment(a);
var b = new Car("ford");
Change(b);
}
public void Increment(int val)
{
val = 10;
}
public void Change(Car car)
{
car.Name = "mercedes"; //this would change the value on the heap, so we are good.
}
So in summary here, within a stack frame, all variables are allocated on the stack, wether reference types or value types. The difference here then is that for reference types, the value being saved on the stack, is the address to the type allocated on the heap. And every value passed to a method is copied by value, which basically is the value on the stack.
So, lets explore one other concept again. in .Net there is a keyword called ref
, you might know it that when used to mark an argument in a method, it indicates that the argument is passed by reference and not value. So what does this mean from our findings above ? this means the value on the stack is exactly what is being passed by reference, independent of wether it is a value type or reference type. So lets see this in code.
public void Main()
{
var a = 5;
Increment(ref a);
var b = new Car("ford");
var c = b;
Change(ref b);
}
public void Increment(ref int val)
{
val = 10;
}
public void Change(ref Car car)
{
car = null;
}
After the execution of the main
method, sure enough, the value of a
is changed to 10, because the ref keyword passes it by reference on the stack. While the value of b
is changed to null
because its value(memory address to the managed heap) on stack is changed. For variable c
, it still points to the value on the managed heap, because even though it was assigned from b
which is now null, they have different spaces on the stack.
In summary, from all these findings, you can use the ref
keyword for value types when you want the called method to change the value (on the stack) and use it with reference types, when you want to replace the value (memory address) on the stack maybe to another new instance.
For engineers from a background of using pointers, you can see how the .Net type system, along with the ref keyword can properly allow you have a "pointers feel" without all the unnecessary problems that comes with pointers.
Top comments (1)
Awesome