When to Use ref vs out Parameters in C#
ref parameters pass arguments by reference and require initialization before the method call. out parameters also pass by reference but don't require prior initialization and must be assigned within the method.
Use out when the method needs to return multiple values or when the input value doesn't matter. Use ref when you want to modify an existing value that was already initialized.
out is commonly used in TryParse patterns where you return success/failure and the parsed value. ref is used when a method needs to update a value in place.
Setup and ref Parameters
C# Example Code
using System;
using System.Collections.Generic;
// ref - must initialize before passing
Console.WriteLine("=== ref parameters ===");
int number = 10;
Console.WriteLine($"Before: {number}");
MultiplyByTwo(ref number);
Console.WriteLine($"After MultiplyByTwo: {number}");
static void MultiplyByTwo(ref int value)
{
value *= 2;
}Using out Parameters
C# Example Code
// out - no need to initialize
Console.WriteLine("\n=== out parameters ===");
int result; // Not initialized
bool success = TryDivide(20, 4, out result);
Console.WriteLine($"Success: {success}");
Console.WriteLine($"Result: {result}");
// out with declaration (C# 7+)
Console.WriteLine("\n=== out with inline declaration ===");
if (TryDivide(100, 5, out int quotient))
{
Console.WriteLine($"100 / 5 = {quotient}");
}
static bool TryDivide(int dividend, int divisor, out int result)
{
if (divisor == 0)
{
result = 0; // Must assign out parameter
return false;
}
result = dividend / divisor;
return true;
}Multiple out Parameters
C# Example Code
// Multiple out parameters
Console.WriteLine("\n=== Multiple out parameters ===");
CalculateStats(new[] { 10, 20, 30, 40, 50 }, out int sum, out double average, out int max);
Console.WriteLine($"Sum: {sum}, Average: {average}, Max: {max}");
static void CalculateStats(int[] numbers, out int sum, out double average, out int max)
{
sum = 0;
max = numbers[0];
foreach (int num in numbers)
{
sum += num;
if (num > max) max = num;
}
average = (double)sum / numbers.Length;
}Using ref for Swapping Values
C# Example Code
// ref for swapping values
Console.WriteLine("\n=== ref for swapping ===");
int a = 5, b = 10;
Console.WriteLine($"Before swap: a={a}, b={b}");
Swap(ref a, ref b);
Console.WriteLine($"After swap: a={a}, b={b}");
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}TryParse Pattern with out
C# Example Code
// Practical example: TryParse pattern
Console.WriteLine("\n=== TryParse pattern with out ===");
string userInput = "123";
if (int.TryParse(userInput, out int parsed))
{
Console.WriteLine($"Parsed successfully: {parsed}");
}
else
{
Console.WriteLine("Failed to parse");
}Using Discards with out
C# Example Code
// out with discards (C# 7+)
Console.WriteLine("\n=== out with discard ===");
if (TryGetUser("alice", out _, out string email))
{
Console.WriteLine($"Email: {email}"); // Only need email, discard ID
}
static bool TryGetUser(string username, out int id, out string email)
{
id = 123;
email = $"{username}@example.com";
return true;
}Using ref with Structs
C# Example Code
// ref with structs - modify in place
Console.WriteLine("\n=== ref with structs ===");
Point point = new Point { X = 10, Y = 20 };
Console.WriteLine($"Before: X={point.X}, Y={point.Y}");
MovePoint(ref point, 5, 5);
Console.WriteLine($"After: X={point.X}, Y={point.Y}");
static void MovePoint(ref Point point, int deltaX, int deltaY)
{
point.X += deltaX;
point.Y += deltaY;
}
public struct Point
{
public int X { get; set; }
public int Y { get; set; }
}Comparing Return Values vs out Parameters
C# Example Code
// Comparing return value vs out
Console.WriteLine("\n=== Return value vs out parameter ===");
// Using return value - single result
int square1 = Square(5);
Console.WriteLine($"Square using return: {square1}");
// Using out - multiple results
SquareAndCube(5, out int square2, out int cube);
Console.WriteLine($"Square: {square2}, Cube: {cube}");
// Tuple return as alternative to out (modern C#)
Console.WriteLine("\n=== Modern alternative: Tuple return ===");
var (squareValue, cubeValue) = SquareAndCubeWithTuple(5);
Console.WriteLine($"Square: {squareValue}, Cube: {cubeValue}");
static int Square(int value)
{
return value * value;
}
static void SquareAndCube(int value, out int square, out int cube)
{
square = value * value;
cube = value * value * value;
}
static (int square, int cube) SquareAndCubeWithTuple(int value)
{
return (value * value, value * value * value);
}Advanced: ref Returns
C# Example Code
// ref return values (advanced)
Console.WriteLine("\n=== ref return (advanced) ===");
int[] numbers = { 1, 2, 3, 4, 5 };
ref int middleElement = ref FindMiddle(numbers);
Console.WriteLine($"Middle element: {middleElement}");
middleElement = 99; // Modifies array directly!
Console.WriteLine($"Array after modification: {string.Join(", ", numbers)}");
static ref int FindMiddle(int[] array)
{
return ref array[array.Length / 2];
}