When to Use foreach vs for Loop in C#

foreach is designed for iterating through collections and is more readable for simple traversal. for loops provide more control with index access, reverse iteration, and custom step sizes.

Use foreach when you need to iterate through all elements of a collection without needing the index. Use for when you need the index, want to iterate in reverse, skip elements, or modify the collection.

foreach is safer (can't access out of bounds) and cleaner for most scenarios. for loops are more flexible and slightly faster with arrays due to bounds checking optimization.

Basic Iteration Comparison

C# Example Code
// Simple iteration - foreach is cleaner
Console.WriteLine("=== Simple iteration - foreach ===");
string[] fruits = { "Apple", "Banana", "Cherry", "Date" };

foreach (string fruit in fruits)
{
    Console.WriteLine(fruit);
}

// Same with for - more verbose
Console.WriteLine("\n=== Same with for loop ===");
for (int i = 0; i < fruits.Length; i++)
{
    Console.WriteLine(fruits[i]);
}

When You Need the Index

C# Example Code
// When you need the index - use for
Console.WriteLine("\n=== When index is needed - use for ===");
for (int i = 0; i < fruits.Length; i++)
{
    Console.WriteLine($"Index {i}: {fruits[i]}");
}

// Alternative: foreach with LINQ Select
Console.WriteLine("\n=== foreach with index using LINQ ===");
foreach (var (fruit, index) in fruits.Select((f, i) => (f, i)))
{
    Console.WriteLine($"Index {index}: {fruit}");
}

Reverse Iteration and Skipping Elements

C# Example Code
// Reverse iteration - for is better
Console.WriteLine("\n=== Reverse iteration - use for ===");
for (int i = fruits.Length - 1; i >= 0; i--)
{
    Console.WriteLine(fruits[i]);
}

// Skip elements - for is better
Console.WriteLine("\n=== Every other element - use for ===");
for (int i = 0; i < fruits.Length; i += 2)
{
    Console.WriteLine(fruits[i]);
}

Modifying Collections During Iteration

C# Example Code
// Modifying collection - careful with foreach!
Console.WriteLine("\n=== Modifying during iteration ===");
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// for loop - safe to modify
for (int i = numbers.Count - 1; i >= 0; i--)
{
    if (numbers[i] % 2 == 0)
    {
        numbers.RemoveAt(i);
    }
}
Console.WriteLine($"After removing evens: {string.Join(", ", numbers)}");

// foreach would throw exception if modifying
// foreach (int num in numbers)  // Don't do this!
// {
//     numbers.Remove(num);  // InvalidOperationException
// }

Nested Loops and Breaking

C# Example Code
// Nested loops - both work
Console.WriteLine("\n=== Nested loops ===");
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

Console.WriteLine("Using for:");
for (int i = 0; i < matrix.GetLength(0); i++)
{
    for (int j = 0; j < matrix.GetLength(1); j++)
    {
        Console.Write($"{matrix[i, j]} ");
    }
    Console.WriteLine();
}

// Breaking out of loops
Console.WriteLine("\n=== Breaking with condition ===");
string[] words = { "hello", "world", "stop", "here", "now" };

Console.WriteLine("foreach with break:");
foreach (string word in words)
{
    if (word == "stop") break;
    Console.WriteLine(word);
}

Console.WriteLine("\nfor with break:");
for (int i = 0; i < words.Length; i++)
{
    if (words[i] == "stop") break;
    Console.WriteLine(words[i]);
}

Performance Comparison

C# Example Code
// Performance comparison
Console.WriteLine("\n=== Performance notes ===");
PerformanceComparison();

static void PerformanceComparison()
{
    int[] largeArray = new int[1000000];
    for (int i = 0; i < largeArray.Length; i++)
    {
        largeArray[i] = i;
    }

    // for loop with array - optimized by compiler
    var watch = System.Diagnostics.Stopwatch.StartNew();
    long sum1 = 0;
    for (int i = 0; i < largeArray.Length; i++)
    {
        sum1 += largeArray[i];
    }
    watch.Stop();
    long forTime = watch.ElapsedMilliseconds;

    // foreach with array
    watch.Restart();
    long sum2 = 0;
    foreach (int num in largeArray)
    {
        sum2 += num;
    }
    watch.Stop();
    long foreachTime = watch.ElapsedMilliseconds;

    Console.WriteLine($"for loop: {forTime}ms");
    Console.WriteLine($"foreach: {foreachTime}ms");
    Console.WriteLine("Performance is similar for arrays (both are optimized)");
}

Using foreach with Different Collection Types

C# Example Code
// foreach with different collection types
Console.WriteLine("\n=== foreach with different collections ===");

// List
List<string> list = new List<string> { "A", "B", "C" };
foreach (string item in list)
{
    Console.Write($"{item} ");
}
Console.WriteLine();

// Dictionary
Dictionary<string, int> dict = new Dictionary<string, int>
{
    { "One", 1 },
    { "Two", 2 }
};
foreach (KeyValuePair<string, int> kvp in dict)
{
    Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}

// HashSet
HashSet<int> set = new HashSet<int> { 1, 2, 3 };
foreach (int num in set)
{
    Console.Write($"{num} ");
}
Console.WriteLine();

Custom Iteration Patterns with for

C# Example Code
// Custom step and conditions with for
Console.WriteLine("\n=== Custom iteration patterns ===");
Console.WriteLine("Every third element:");
for (int i = 0; i < 20; i += 3)
{
    Console.Write($"{i} ");
}
Console.WriteLine();

// Multiple variables in for
Console.WriteLine("\nTwo variables:");
for (int i = 0, j = 10; i < 5; i++, j--)
{
    Console.WriteLine($"i={i}, j={j}");
}

Usage Guidelines

Use foreach when:

  • Iterating all elements in order
  • Don't need the index
  • Not modifying the collection
  • Working with IEnumerable

Use for when:

  • Need the index
  • Iterating backwards
  • Custom step size
  • Modifying collection during iteration
  • Need maximum performance with arrays