Loops are one of the most powerful building blocks in C#. They allow you to repeat logic, process collections, run calculations, and automate repetitive tasks.
Understanding loops deeply helps you write cleaner, faster, and more efficient code — and avoid performance pitfalls.
In this article, we’ll explore all major loops in C#, their differences, use cases, and the mistakes developers commonly make.
1. for Loop
The for loop is the most commonly used loop when the number of iterations is known.
Example
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Iteration: " + i);
}
When to Use
When you know the starting and ending point
When iterating using index
Useful for arrays, numeric sequences, and performance-sensitive loops.
Common Mistake
❌ Forgetting to increment i → leads to infinite loop
❌ Using wrong boundary → off-by-one errors
❌ Doing heavy operations inside loop condition:
for (int i = 0; i < expensiveMethod(); i++) { }
2. while Loop
A while loop repeats as long as the condition is true.
Example
int i = 0;
while (i < 5)
{
Console.WriteLine(i);
i++;
}
When to Use
When the number of iterations is not known
When looping depends on an external condition (file stream open, user input, etc.)
Common Mistake
❌ Forgetting to modify the condition variable → infinite loop
❌ Using while when for loop is more readable
3. do-while Loop
Similar to while, but it always executes at least once.
Example
int i = 0;
do
{
Console.WriteLine(i);
i++;
}
while (i < 5);
When to Use
When code must run at least once
Example: Login prompt, menu system, user input validation
Common Mistake
❌ Using do-while unnecessarily when condition should be checked first
4. foreach Loop
Designed to iterate collections (List, Array, Dictionary, etc.) in a clean and safe way.
Example
var names = new List<string> { "John", "Asha", "Ravi" };
foreach (var name in names)
{
Console.WriteLine(name);
}
When to Use
When iterating collections
When you don’t need index
When you want safe and clean iteration
Common Mistake
❌ Modifying collection inside foreach → throws exception
❌ Using for loop instead of foreach, hurting readability
❌ Using foreach on heavy large dataset instead of for (slightly slower)
5. Parallel Loops (Advanced)
C# provides special loops for performance, especially with large data.
5.1 Parallel.For
Executes loop iterations in parallel across multiple threads.
Parallel.For(0, 10, i =>
{
Console.WriteLine($"Index: {i}");
});
5.2 Parallel.ForEach
Parallel version of foreach.
Parallel.ForEach(myList, item =>
{
Console.WriteLine(item);
});
When to Use
Common Mistake
❌ Using parallel loops for small tasks → slower
❌ Modifying shared variables without locks → thread safety issues
6. LINQ Loops (Functional Style)
Not traditional loops — but used for iteration internally.
Example
var filtered = list.Where(x => x > 10);
When to Use
When filtering, selecting, projecting data
When you want readable “query-style” code
When writing clean and expressive logic
Common Mistake
❌ Using LINQ inside tight loops can be slow
❌ Calling .ToList() unnecessarily
❌ Complex lambdas reduce readability
7. break, continue, and return
Sometimes controlling loop flow is required.
break
Stops loop completely.
continue
Skips current iteration.
return
Exits the method entirely.
Common Mistake
❌ Overusing break/continue → reduces readability
❌ Using return deep inside loop → unexpected behavior
8. Infinite Loops (Intentional)
Sometimes used in long-running systems.
Example
while(true)
{
ListenToQueue();
}
When to Use
Background services
Server listeners
Real-time systems
Common Mistake
❌ No exit condition
❌ Not adding await Task.Delay() → high CPU usage
🔍 Differences Between All Loops
| Loop Type | Executes At Least Once? | Best Use Case | Has Index? | Typical Mistake |
|---|
| for | ❌ | When count is known | ✔ | Wrong index, infinite loop |
| while | ❌ | Unknown iterations | ✔ Optional | Forgot increment |
| do-while | ✔ | UI input, prompts | ✔ Optional | Unnecessary use |
| foreach | ❌ | Collections | ❌ | Modifying collection |
| Parallel.For | ❌ | CPU tasks | ✔ | Not thread-safe |
| Parallel.ForEach | ❌ | Big list processing | ❌ | Performance misuse |
| LINQ loops | ❌ | Query operations | ❌ | Overuse, performance issues |
1) C# Benchmark program (copy → build → run)
Run this on your machine (Visual Studio / dotnet run) — it will measure each loop over 10,000 / 100,000 / 500,000 / 1,000,000 records, repeat each measurement several times, produce average times (ms) and write CSV files you can use for charts/GIFs.
// File: LoopBenchmarks/Program.cs
// .NET 7+ recommended. Create console app: `dotnet new console -n LoopBenchmarks`
// Replace Program.cs with this code, then `dotnet run -c Release` for better timings.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using System.Collections.Concurrent;
class Program
{
static void Main()
{
// record sizes (you selected Option A)
int[] sizes = new int[] { 10_000, 100_000, 500_000, 1_000_000 };
int warmups = 2;
int iterations = 3; // measure each loop N times and average
var results = new List<(string LoopName, int Records, double TimeMs)>();
Console.WriteLine("Preparing datasets...");
var datasets = new Dictionary<int, int[]>();
foreach (var n in sizes)
datasets[n] = Enumerable.Range(0, n).ToArray();
foreach (var size in sizes)
{
var data = datasets[size];
Console.WriteLine($"\n== Measuring for {size} records ==");
// Warmup (JIT)
for (int w=0; w<warmups; w++)
{
ForIndex(data);
ForEachLoop(data);
WhileLoop(data);
DoWhileLoop(data);
ListComprehensionLike(data);
MapLike(data);
ParallelForLoop(data);
ParallelForEachLoop(data);
}
// measure
results.AddRange(MeasureLoop("for_index", size, iterations, () => ForIndex(data)));
results.AddRange(MeasureLoop("for_each", size, iterations, () => ForEachLoop(data)));
results.AddRange(MeasureLoop("while", size, iterations, () => WhileLoop(data)));
results.AddRange(MeasureLoop("do_while", size, iterations, () => DoWhileLoop(data)));
results.AddRange(MeasureLoop("linq_select_sum", size, iterations, () => ListComprehensionLike(data)));
results.AddRange(MeasureLoop("map_sum", size, iterations, () => MapLike(data)));
results.AddRange(MeasureLoop("parallel_for", size, iterations, () => ParallelForLoop(data)));
results.AddRange(MeasureLoop("parallel_foreach", size, iterations, () => ParallelForEachLoop(data)));
}
// compute averages and write CSV
var grouped = results
.GroupBy(r => (r.LoopName, r.Records))
.Select(g => new {
Loop = g.Key.LoopName,
Records = g.Key.Records,
AvgMs = g.Average(x => x.TimeMs)
})
.OrderBy(x => x.Records).ThenBy(x => x.Loop)
.ToList();
string csv = "Loop,Records,AvgMs\n" + string.Join("\n", grouped.Select(g => $"{g.Loop},{g.Records},{g.AvgMs:F4}"));
File.WriteAllText("loop_benchmarks.csv", csv);
Console.WriteLine("\nBenchmark complete. CSV written to ./loop_benchmarks.csv\n");
Console.WriteLine(csv);
}
static IEnumerable<(string LoopName, int Records, double TimeMs)> MeasureLoop(string name, int records, int iterations, Action action)
{
var sw = new Stopwatch();
// Ensure GC baseline
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
Thread.Sleep(50);
List<double> times = new List<double>();
for (int i=0; i<iterations; i++)
{
sw.Restart();
action();
sw.Stop();
times.Add(sw.Elapsed.TotalMilliseconds);
Thread.Sleep(20);
}
double avg = times.Average();
yield return (name, records, avg);
}
// Loop implementations
static long ForIndex(int[] data)
{
long s = 0;
for (int i=0; i < data.Length; i++)
{
s += (long)data[i] * data[i];
}
return s;
}
static long ForEachLoop(int[] data)
{
long s = 0;
foreach (var x in data)
{
s += (long)x * x;
}
return s;
}
static long WhileLoop(int[] data)
{
long s = 0;
int i = 0;
while (i < data.Length)
{
s += (long)data[i] * data[i];
i++;
}
return s;
}
static long DoWhileLoop(int[] data)
{
long s = 0;
int n = data.Length;
if (n == 0) return 0;
int i = 0;
do
{
s += (long)data[i] * data[i];
i++;
} while (i < n);
return s;
}
static long ListComprehensionLike(int[] data)
{
// LINQ Select + Sum
return data.Select(x => (long)x * x).Sum();
}
static long MapLike(int[] data)
{
// functional-like: same as above but using simple loop to simulate map
long s = 0;
foreach (var x in data)
s += (long)x * x;
return s;
}
static long ParallelForLoop(int[] data)
{
long total = 0;
object lockObj = new object();
System.Threading.Tasks.Parallel.For(0, data.Length, () => 0L,
(i, state, local) =>
{
local += (long)data[i] * data[i];
return local;
},
local =>
{
lock (lockObj) total += local;
});
return total;
}
static long ParallelForEachLoop(int[] data)
{
// Split in partitions to reduce synchronization cost
long total = 0;
object lockObj = new object();
var rangePartitioner = System.Collections.Concurrent.Partitioner.Create(0, data.Length);
System.Threading.Tasks.Parallel.ForEach(rangePartitioner, () => 0L, (range, state, local) =>
{
for (int i = range.Item1; i < range.Item2; i++)
local += (long)data[i] * data[i];
return local;
}, local =>
{
lock (lockObj) total += local;
});
return total;
}
}
How to run (recommended):
Build in Release configuration: dotnet run -c Release (release gives more realistic timings).
After run, open loop_benchmarks.csv and use it to generate charts (Excel, Google Sheets, or image tools).
Optionally run multiple times on your machine and average across runs.
2) Illustrative performance table (ESTIMATED)
These are illustrative numbers (ms) on a typical modern 4–8 core laptop. Your machine will differ. Use the C# program above to obtain precise numbers for your article. The table shows expected ordering and magnitudes.
| Loop Type | 10k rec (ms) | 100k rec (ms) | 500k rec (ms) | 1,000k rec (ms) | Expected relative |
|---|
| for (index over array) | 0.6 | 6 | 30 | 60 | Very fast — minimal overhead |
| foreach (List/Array) | 0.8 | 8 | 40 | 80 | Slightly slower than indexed for arrays; still very fast |
| while | 0.7 | 7 | 35 | 70 | Comparable to for (index) |
| do-while | 0.7 | 7 | 35 | 70 | Same as while for CPU-bound simple body |
| LINQ (Select + Sum) | 3.5 | 35 | 180 | 360 | Noticeably slower — allocations & delegates |
| map-like (functional with lambda) | 2.0 | 20 | 100 | 200 | Slower than raw loops due to lambda overhead |
| Parallel.For / Parallel.ForEach | 6 | 20 | 40 | 50 | Slower on tiny data (10k), faster for large sizes (500k+) on multi-core — benefits depend on workload & thread overhead |
Key expected takeaways
For very small datasets (10k), overheads dominate — raw single-threaded loops are fastest. Parallel often slower due to thread scheduling.
For medium-large (500k, 1,000k) CPU-bound work, Parallel.For/Parallel.ForEach can be fastest — but only if the loop body is heavier than a few CPU cycles and you handle thread-safety correctly.
LINQ (Select, Where, ToList, Sum) often introduces allocations, delegate calls and can be 2–5x slower than a raw for/foreach for simple operations. LINQ is great for readability and composition, but pay attention in hot paths.
foreach on List<T> and for on array are both optimized in .NET — for arrays for sometimes edges out, for List<T> foreach is generally very competitive.