When you call a method in C#, parameters are passed by value by default. This means the method gets a copy of the variable, and changes made inside the method do not affect the original variable outside it.
Sometimes, you want the method to work directly with the original variable. This is where ref, out, and in parameters come in. These allow you to pass variables by reference, meaning both the method and the caller use the same memory location.
C# ref Parameter – Pass by Reference with Read/Write Access
The ref keyword lets you pass a variable by reference, allowing the method to read and modify its value. The variable must be initialized before being passed.
A real-world example could be a bank interest calculation. The method takes the current balance and updates it after applying interest.
using System;
public class Bank
{
public void ApplyInterest(ref decimal balance, decimal interestRate)
{
balance += balance * interestRate / 100;
Console.WriteLine($"Balance after applying interest: {balance}");
}
}
public class Program
{
public static void Main()
{
decimal accountBalance = 1000m;
Bank bank = new Bank();
Console.WriteLine($"Initial Balance: {accountBalance}");
bank.ApplyInterest(ref accountBalance, 5);
Console.WriteLine($"Final Balance: {accountBalance}");
}
}
The advantage of ref
is that the method can modify the variable without needing to return it.
The disadvantage is that changes may happen unexpectedly, and the variable must be initialized before passing.
C# out Parameter – Pass by Reference for Output Only
The out keyword is used when a method will set the value of a variable before it returns. The variable does not need to be initialized beforehand.
This is often useful when returning multiple values. For example, a login method could return both a success flag and the user's role.
using System;
public class AuthService
{
public bool Login(string username, string password, out string role)
{
if (username == "admin" && password == "1234")
{
role = "Administrator";
return true;
}
else if (username == "john" && password == "pass")
{
role = "User";
return true;
}
else
{
role = "Guest";
return false;
}
}
}
public class Program
{
public static void Main()
{
AuthService auth = new AuthService();
if (auth.Login("admin", "1234", out string userRole))
{
Console.WriteLine($"Login successful! Role: {userRole}");
}
else
{
Console.WriteLine("Login failed.");
}
}
}
The benefit of out
is that it allows multiple return values.
The downside is that the method must always assign a value to it, which can sometimes make code harder to maintain.
C# in Parameter – Pass by Reference with Read-Only Access
The in keyword passes a variable by reference but makes it read-only inside the method. This ensures that the value cannot be modified.
It's helpful for large structs where copying would be expensive. For example, calculating the distance between two coordinates.
using System;
public struct Coordinates
{
public double X { get; }
public double Y { get; }
public Coordinates(double x, double y)
{
X = x;
Y = y;
}
}
public class GeoService
{
public double CalculateDistance(in Coordinates point1, in Coordinates point2)
{
double dx = point2.X - point1.X;
double dy = point2.Y - point1.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
public class Program
{
public static void Main()
{
Coordinates location1 = new Coordinates(0, 0);
Coordinates location2 = new Coordinates(3, 4);
GeoService geo = new GeoService();
double distance = geo.CalculateDistance(in location1, in location2);
Console.WriteLine($"Distance: {distance}");
}
}
The main advantage is performance and safety.
The drawback is that it is most useful for large data types and provides little benefit for small ones.
Conclusion
While ref
, out
, and in
all pass variables by reference; they serve different purposes. ref
is for read/write, out
is for output-only, and in
is read-only. Use them wisely, as overuse can make your code harder to read and debug. They are powerful tools in C# when used in the right scenarios.