×

C# Crash Course

Course Home C# Introduction and Setup C# Variables and Data Types C# Console Input and Output C# Operators and Expressions C# Conditional Statements - If and Else C# Switch Statements C# While and Do-While Loops C# For Loops C# Nested Loops C# Methods - Part 1 (Basics) C# Methods - Part 2 (ref, out, and Recursion) C# Arrays C# Number Systems (Binary and Hexadecimal) C# Exception Handling (Try-Catch) C# Random Numbers C# String Methods and Manipulation C# Course Summary and Best Practices


C# Methods - Part 2 (ref, out, and Recursion)

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