When transitioning from the JavaScript ecosystem to C# and .NET, there are plenty of paradigm shifts. But one of the most common traps and a concept that completely tripped me up recently is understanding how parameters are actually passed.
If you asked me a few weeks ago, "How are reference types passed in C#?" I would have confidently answered: "By reference, obviously." It’s right there in the name! But diving deeper into the language, I realized that answer is technically wrong. In C#, everything is passed by value by default. Yes, even Reference Types.
Let’s break down this illusion, look at what’s actually happening in memory, and explore a C# feature that we simply don't have in standard JavaScript.
The Illusion: Modifying Properties
In JavaScript, if you pass an object into a function and change its property, that change reflects outside the function. C# behaves the exact same way.
public class User
{
public string Name { get; set; }
}
public void UpdateName(User u)
{
u.Name = "Alice";
}
// Usage:
User myUser = new User { Name = "Bob" };
UpdateName(myUser);
Console.WriteLine(myUser.Name); // Output: Alice
Because this behaves exactly like JavaScript, it reinforced my assumption that myUser was passed by reference. But it wasn't.
The Reality: Passing a Copy of the Pointer
When you pass a Reference Type into a method in C#, you aren't passing the actual object. You are passing a copy of the reference (the pointer) to that object.
Think of it like giving a friend a copy of a treasure map. If they use their map to dig a hole on the island (modifying a property), the physical island changes. But if they take their copy of the map, cross out the island, and draw a completely different island on the paper (reassigning the object), your original map remains perfectly intact.
Here is where the trap caught me:
public void ReassignUser(User u)
{
// Pointing the local copy of the reference to a brand new object
u = new User { Name = "Charlie" };
}
// Usage:
User myUser = new User { Name = "Bob" };
ReassignUser(myUser);
Console.WriteLine(myUser.Name); // Output: Bob (Wait, what?)
Inside ReassignUser, the local variable u is pointing to a completely new object on the heap. But myUser back in the calling method? It’s still pointing to "Bob". The reference was passed by value, and the method only reassigned the copy.
The ref Keyword: A C# Superpower
So, how do you actually pass by reference in C#? You have to be explicit. C# provides the ref keyword, a concept I hadn't encountered in Node.js or Express.
When you use ref, you are passing a reference to the original reference. You are handing over your original treasure map, not a copy.
public void ActuallyReassignUser(ref User u)
{
u = new User { Name = "Charlie" };
}
// Usage:
User myUser = new User { Name = "Bob" };
ActuallyReassignUser(ref myUser); // Notice the ref keyword here too
Console.WriteLine(myUser.Name); // Output: Charlie
Now, when the method points u to a new object, myUser is forced to look at that new object too.
The Takeaway
As someone whose heart has always been in the MERN stack, adjusting to the strictness of C# has been an incredible learning experience. Understanding this memory management distinction was a major lightbulb moment for me.
If you are also exploring the .NET world, keep this golden rule in mind: Unless you see the ref or out keywords, C# is passing a copy. ---
Related Tags
#csharp#dotnet#webdevelopment#codingjourney
