Parameter Passing: Value vs Reference
By default, parameters are passed by value (a copy is made).
Pass by Value (Default)
static void Main(string[] args)
{
int iNumber = 10;
Console.WriteLine($"Before: {iNumber}");
ChangeValue(iNumber);
Console.WriteLine($"After: {iNumber}"); // Still 10!
Console.ReadKey();
}
static void ChangeValue(int iNum)
{
iNum = 20; // Changes only the copy
Console.WriteLine($"Inside method: {iNum}");
}
// Output:
// Before: 10
// Inside method: 20
// After: 10
The ref Keyword
The ref
keyword passes a parameter by reference, allowing the method to modify the original variable.
Basic ref Example
static void Main(string[] args)
{
int iNumber = 10;
Console.WriteLine($"Before: {iNumber}");
ChangeValue(ref iNumber);
Console.WriteLine($"After: {iNumber}"); // Changed to 20!
Console.ReadKey();
}
static void ChangeValue(ref int iNum)
{
iNum = 20; // Changes the original variable
}
// Output:
// Before: 10
// After: 20
ref Requirements
✅ Variable must be initialized before passing
✅ Must use ref
in both method declaration and call
✅ Cannot use with constants
// WRONG - not initialized
int iX;
ChangeValue(ref iX); // ERROR!
// CORRECT
int iY = 5;
ChangeValue(ref iY); // OK
// WRONG - missing ref in call
ChangeValue(iY); // ERROR! Must use ref
// WRONG - cannot use with constant
const int iZ = 10;
ChangeValue(ref iZ); // ERROR!
The out Keyword
The out
keyword is similar to ref
, but doesn't require the variable to be initialized first. The method must assign a value to it.
Basic out Example
static void Main(string[] args)
{
int iResult; // No initialization needed!
Calculate(5, 3, out iResult);
Console.WriteLine($"Result: {iResult}");
Console.ReadKey();
}
static void Calculate(int iA, int iB, out int iSum)
{
iSum = iA + iB; // Must assign value
}
Multiple out Parameters
static void Main(string[] args)
{
int iSum, iProduct;
Calculate(5, 3, out iSum, out iProduct);
Console.WriteLine($"Sum: {iSum}");
Console.WriteLine($"Product: {iProduct}");
Console.ReadKey();
}
static void Calculate(int iA, int iB, out int iSum, out int iProduct)
{
iSum = iA + iB;
iProduct = iA * iB;
}
// Output:
// Sum: 8
// Product: 15
Practical Example from Course: UpdateStatistics (Worksheet 6)
using System;
namespace StatisticsUpdater
{
class Program
{
static void Main(string[] args)
{
int iSum = 0;
int iCount = 0;
double dAverage;
do
{
int iNum = GetInt($"Enter integer {iCount + 1} (1-20): ");
UpdateStatistics(iNum, ref iCount, ref iSum, out dAverage);
DisplayStatistics(iSum, dAverage);
} while (iSum <= 100);
Console.WriteLine("\nThe sum now exceeds 100.");
Console.Write("\nPress any key to exit...");
Console.ReadKey();
}
static int GetInt(string sPrompt)
{
int iValue;
bool isValid;
do
{
Console.Write(sPrompt);
isValid = int.TryParse(Console.ReadLine(), out iValue);
if (!isValid || iValue < 1 || iValue > 20)
{
Console.WriteLine("Invalid input. Enter a number between 1 and 20.\n");
isValid = false;
}
} while (!isValid);
return iValue;
}
static void UpdateStatistics(int iNum, ref int iCount, ref int iSum, out double dAverage)
{
iCount++; // Update count (ref)
iSum += iNum; // Update sum (ref)
dAverage = (double)iSum / iCount; // Calculate average (out)
}
static void DisplayStatistics(int iSum, double dAverage)
{
Console.WriteLine($"\nCurrent Sum: {iSum}");
Console.WriteLine($"Current Average: {dAverage:0.00}\n");
}
}
}
ref vs out Comparison
Feature | ref | out |
---|---|---|
Must initialize before passing | ✅ Yes | ❌ No |
Must assign in method | ❌ Optional | ✅ Required |
Used for | Modifying existing values | Getting multiple return values |
When to Use ref
Use ref
when you want to modify an existing variable:
static void Swap(ref int iA, ref int iB)
{
int iTemp = iA;
iA = iB;
iB = iTemp;
}
int iX = 5, iY = 10;
Console.WriteLine($"Before: X={iX}, Y={iY}");
Swap(ref iX, ref iY);
Console.WriteLine($"After: X={iX}, Y={iY}");
// Output:
// Before: X=5, Y=10
// After: X=10, Y=5
When to Use out
Use out
when you want to return multiple values:
static void GetMinMax(int[] iNumbers, out int iMin, out int iMax)
{
iMin = iNumbers[0];
iMax = iNumbers[0];
foreach (int iNum in iNumbers)
{
if (iNum < iMin) iMin = iNum;
if (iNum > iMax) iMax = iNum;
}
}
int[] iData = { 5, 2, 9, 1, 7 };
int iMinimum, iMaximum;
GetMinMax(iData, out iMinimum, out iMaximum);
Console.WriteLine($"Min: {iMinimum}, Max: {iMaximum}");
// Output: Min: 1, Max: 9
Recursion
Recursion is when a method calls itself. It's useful for problems that can be broken down into smaller similar problems.
Basic Recursion Structure
static void RecursiveMethod(parameters)
{
if (base case)
{
// Stop recursion
return;
}
else
{
// Recursive case
RecursiveMethod(modified parameters); // Call itself
}
}
Example 1: Countdown
static void Main(string[] args)
{
Countdown(5);
Console.WriteLine("Blast off!");
Console.ReadKey();
}
static void Countdown(int iN)
{
if (iN == 0)
{
return; // Base case: stop
}
Console.WriteLine(iN);
Countdown(iN - 1); // Recursive call
}
// Output:
// 5
// 4
// 3
// 2
// 1
// Blast off!
Example 2: Factorial
Factorial: n! = n × (n-1) × (n-2) × ... × 1 Example: 5! = 5 × 4 × 3 × 2 × 1 = 120
static void Main(string[] args)
{
int iResult = Factorial(5);
Console.WriteLine($"5! = {iResult}");
Console.ReadKey();
}
static int Factorial(int iN)
{
if (iN == 0 || iN == 1)
{
return 1; // Base case
}
return iN * Factorial(iN - 1); // Recursive case
}
// How it works:
// Factorial(5) = 5 * Factorial(4)
// = 5 * 4 * Factorial(3)
// = 5 * 4 * 3 * Factorial(2)
// = 5 * 4 * 3 * 2 * Factorial(1)
// = 5 * 4 * 3 * 2 * 1
// = 120
Recursive Examples from Course
Example 1: Sum Using Recursion (From Practical Test 5)
using System;
namespace RecursiveSum
{
class Program
{
static void Main(string[] args)
{
try
{
Console.Write("Enter a: ");
int iA = int.Parse(Console.ReadLine());
Console.Write("Enter b: ");
int iB = int.Parse(Console.ReadLine());
int iSum = Sum(iA, iB);
Console.WriteLine($"\nThe sum of {iA} and {iB} is {iSum}");
}
catch
{
Console.WriteLine("\nInvalid input.");
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static int Sum(int iA, int iB)
{
if (iA == 0)
{
return iB;
}
else if (iB == 0)
{
return iA;
}
else
{
return Sum(iA - 1, iB) + 1;
}
}
}
}
// How Sum(3, 5) works:
// Sum(3, 5) = Sum(2, 5) + 1
// = Sum(1, 5) + 1 + 1
// = Sum(0, 5) + 1 + 1 + 1
// = 5 + 1 + 1 + 1
// = 8
Example 2: Division Using Recursion (From Worksheet 5)
using System;
namespace RecursiveDivision
{
class Program
{
static void Main(string[] args)
{
try
{
Console.Write("First number: ");
int iA = int.Parse(Console.ReadLine());
Console.Write("Second number: ");
int iB = int.Parse(Console.ReadLine());
int iQuotient = Quotient(iA, iB);
Console.WriteLine($"\nThe quotient of {iA} / {iB} is {iQuotient}");
}
catch
{
Console.WriteLine("\nInvalid input");
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static int Quotient(int iA, int iB)
{
if (iA < iB)
{
return 0; // Base case
}
else
{
return 1 + Quotient(iA - iB, iB);
}
}
}
}
// How Quotient(10, 3) works:
// Quotient(10, 3) = 1 + Quotient(7, 3)
// = 1 + 1 + Quotient(4, 3)
// = 1 + 1 + 1 + Quotient(1, 3)
// = 1 + 1 + 1 + 0
// = 3
Example 3: Even/Odd Check Using Recursion (From Worksheet 5)
using System;
namespace RecursiveEvenOdd
{
class Program
{
static void Main(string[] args)
{
Console.Write("Enter a number: ");
if (int.TryParse(Console.ReadLine(), out int iNumber))
{
string sMsg;
if (IsEven(iNumber))
{
sMsg = "even number";
}
else
{
sMsg = "odd number";
}
Console.WriteLine($"This is an {sMsg}");
}
else
{
Console.WriteLine("Invalid input");
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static bool IsEven(int iNumber)
{
if (iNumber == 0)
{
return true; // 0 is even
}
else if (iNumber < 0)
{
return false; // Negative treated as false
}
else
{
return IsEven(iNumber - 2); // Subtract 2 recursively
}
}
}
}
// How IsEven(6) works:
// IsEven(6) = IsEven(4)
// = IsEven(2)
// = IsEven(0)
// = true
Recursion vs Iteration
Recursive Approach
static int SumRecursive(int iN)
{
if (iN == 0)
return 0;
return iN + SumRecursive(iN - 1);
}
Iterative Approach (Using Loop)
static int SumIterative(int iN)
{
int iSum = 0;
for (int i = 1; i <= iN; i++)
{
iSum += i;
}
return iSum;
}
When to Use Recursion
✅ Good for: - Tree/graph traversal - Factorial, Fibonacci - Divide and conquer algorithms - Problems that are naturally recursive
❌ Avoid for: - Simple loops (use for/while instead) - Large datasets (risk stack overflow) - When performance is critical
Important Recursion Concepts
1. Base Case (Stopping Condition)
Every recursive method MUST have a base case to stop recursion.
// WRONG - No base case! Infinite recursion!
static int BadMethod(int iN)
{
return iN + BadMethod(iN - 1); // Never stops!
}
// CORRECT - Has base case
static int GoodMethod(int iN)
{
if (iN == 0) // Base case
return 0;
return iN + GoodMethod(iN - 1);
}
2. Recursive Case
The part that calls the method itself with modified parameters.
static int Factorial(int iN)
{
if (iN <= 1)
return 1; // Base case
return iN * Factorial(iN - 1); // Recursive case
}
3. Stack Overflow
Too many recursive calls can crash your program:
// Dangerous! Large numbers cause stack overflow
static int Factorial(int iN)
{
if (iN <= 1)
return 1;
return iN * Factorial(iN - 1);
}
// Factorial(100000) will crash!
Practice Exercises
Exercise 1: Power Function
Create a recursive method that calculates x^n (x to the power of n).
Exercise 2: Count Digits
Create a recursive method that counts digits in a number. Example: CountDigits(12345) = 5
Exercise 3: Sum of Array
Create a recursive method that sums all elements in an array.
Exercise 4: Reverse String
Create a recursive method that reverses a string.
Exercise 5: GCD (Greatest Common Divisor)
Create a recursive method using Euclidean algorithm.
Common Mistakes to Avoid
❌ Missing base case:
static int Sum(int iN)
{
return iN + Sum(iN - 1); // WRONG! Infinite recursion
}
✅ Correct:
static int Sum(int iN)
{
if (iN == 0)
return 0; // Base case
return iN + Sum(iN - 1);
}
❌ Forgetting ref/out in method call:
static void Change(ref int iX)
{
iX = 10;
}
int iNum = 5;
Change(iNum); // WRONG! Missing ref
✅ Correct:
int iNum = 5;
Change(ref iNum); // Correct
❌ Not assigning out parameter:
static void Calculate(int iA, int iB, out int iSum)
{
// WRONG! Must assign iSum
}
✅ Correct:
static void Calculate(int iA, int iB, out int iSum)
{
iSum = iA + iB; // Must assign
}
Advanced Example: Fibonacci Sequence
using System;
namespace Fibonacci
{
class Program
{
static void Main(string[] args)
{
Console.Write("How many Fibonacci numbers? ");
int iN = int.Parse(Console.ReadLine());
Console.WriteLine("\nFibonacci Sequence:");
for (int i = 0; i < iN; i++)
{
Console.Write(Fibonacci(i) + " ");
}
Console.WriteLine("\n\nPress any key to exit...");
Console.ReadKey();
}
static int Fibonacci(int iN)
{
if (iN == 0)
return 0;
if (iN == 1)
return 1;
return Fibonacci(iN - 1) + Fibonacci(iN - 2);
}
}
}
// Output for n=10:
// 0 1 1 2 3 5 8 13 21 34
Complete Example: Number Category Checker (Practical Test 6)
using System;
namespace NumberCategory
{
class Program
{
static void Main(string[] args)
{
bool isValid = true;
string sProperDivisors = "";
int iNum = GetNumber("Enter a positive integer: ", ref isValid);
if (isValid)
{
int iCategory = GetCategory(iNum, ref sProperDivisors, out int iSum);
Console.WriteLine($"\nProper divisors of {iNum}: {sProperDivisors}");
Console.WriteLine($"Sum of divisors: {iSum}");
switch (iCategory)
{
case -1:
Console.WriteLine($"{iNum} is a deficient number.");
break;
case 0:
Console.WriteLine($"{iNum} is an abundant number.");
break;
case 1:
Console.WriteLine($"{iNum} is a perfect number.");
break;
}
}
Console.WriteLine("\nPress any key to exit...");
Console.ReadKey();
}
static int GetNumber(string sPrompt, ref bool isValid)
{
Console.Write(sPrompt);
if (int.TryParse(Console.ReadLine(), out int iNum))
{
if (iNum > 0)
{
isValid = true;
return iNum;
}
}
Console.WriteLine("Invalid input, the number is not a positive integer.");
isValid = false;
return 0;
}
static int GetCategory(int iNum, ref string sDivisors, out int iSum)
{
iSum = 0;
sDivisors = "";
// Find all proper divisors
for (int i = 1; i < iNum; i++)
{
if (iNum % i == 0)
{
iSum += i;
sDivisors += i + " ";
}
}
// Determine category
if (iSum < iNum)
return -1; // Deficient
else if (iSum > iNum)
return 0; // Abundant
else
return 1; // Perfect
}
}
}
Key Takeaways
✅ ref: Pass by reference, variable must be initialized
✅ out: Return multiple values, doesn't need initialization
✅ Use ref
in both declaration and call
✅ Method must assign value to out
parameter
✅ Recursion: Method calls itself
✅ Always have a base case to stop recursion
✅ Recursion is elegant but can be slow/dangerous
✅ Use try-catch when parsing user input
Next Topic: C# Arrays