TheDocumentation Index
Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt
Use this file to discover all available pages before exploring further.
foreach statement is an iteration construct that enumerates the elements of a collection. It provides a forward-only traversal of any type that implements the System.Collections.IEnumerable, System.Collections.Generic.IEnumerable<T>, or System.Collections.Generic.IAsyncEnumerable<T> interfaces, or any type that satisfies the C# enumerable pattern.
Syntax
await: An optional keyword used for asynchronous iteration over collections implementingIAsyncEnumerable<T>or the asynchronous enumerable pattern.Type: The data type of the elements in the collection. The implicitly typed local variablevarcan be used to instruct the compiler to infer the type.iterationVariable: A local variable representing the current element in the iteration. By default, this variable is read-only. If the collection’s enumerator yields references, it can be declared with thereforref readonlymodifier to allow direct mutation or avoid copying.collection: The expression representing the iterable data structure.- Tuple Deconstruction: Introduced in C# 7.0, the
foreachloop supports deconstructing tuples or types with aDeconstructmethod (such asKeyValuePair<TKey, TValue>) directly in the iteration variable declaration.
Compiler Expansion (Under the Hood)
Theforeach loop is syntactic sugar. At compile time, the C# compiler translates the foreach statement into lower-level constructs based on the type of the collection being iterated.
Array Optimization
When iterating over a single-dimensional, zero-based array (SZArray), the compiler completely bypassesGetEnumerator() and IEnumerator. To maximize performance and avoid allocation, it expands the foreach statement into a standard for loop using an indexer:
Enumerator Expansion
For non-array collections, the compiler translates theforeach statement into a while loop that explicitly manages the enumerator.
Crucially, the compiler binds to the exact type returned by GetEnumerator(). It does not cast the enumerator to IEnumerator<T> or IEnumerator. Many Base Class Library (BCL) collections (such as List<T> or Span<T>) return a struct enumerator. By binding to the exact struct type, the compiler avoids boxing allocations that would occur if the struct were cast to an interface.
finally block based on the enumerator’s compile-time type:
- Compile-time
IDisposable: If the enumerator type explicitly implementsIDisposable(likeIEnumerator<T>), the compiler generates a direct call toDispose(). For value types, it emits theconstrained.IL instruction to invokeDispose()without boxing the struct. - Pattern-Based Disposal (
ref struct): Becauseref structtypes cannot implement interfaces, C# 8.0 and later use pattern matching for disposal. If theref structenumerator has a publicvoid Dispose()method, the compiler generates afinallyblock to invoke it directly. - Compile-time Non-Disposable: If the enumerator is a
structor asealedclass that neither implementsIDisposablenor has a pattern-basedDispose()method, the compiler knows at compile time that it cannot be disposed. It completely omits thefinallyblock. - Runtime Check Required: If the enumerator is an unsealed class or an interface (like the non-generic
IEnumerator) where the implementation is unknown at compile time, the compiler generates a runtime type check (conceptuallyif (enumerator is IDisposable disposable)).
The Enumerable Pattern (Duck Typing)
C# does not strictly require a collection to implementIEnumerable or IEnumerable<T> for foreach to function. The compiler uses pattern matching (duck typing) to resolve the iteration. A type can be iterated over if it satisfies the following patterns:
Synchronous Pattern (foreach):
- It contains a public method named
GetEnumerator()that can be invoked with no arguments. This requirement is satisfied by a parameterless method, a method where all parameters have default values (optional parameters), or an extension method. - The return type of
GetEnumerator()has a public method namedMoveNext()that can be invoked with no arguments (including methods utilizing optional parameters) and returns abool. - The return type of
GetEnumerator()has a public property namedCurrentequipped with agetaccessor.
await foreach):
- It contains a public method named
GetAsyncEnumerator()that can be invoked with no arguments (including methods with optional parameters or extension methods). - The return type of
GetAsyncEnumerator()has a public method namedMoveNextAsync()that can be invoked with no arguments (including methods with optional parameters). The return type ofMoveNextAsync()must be an awaitable type whose awaiter yields aboolresult (such asValueTask<bool>,Task<bool>, or a custom awaitable type). - The return type of
GetAsyncEnumerator()has a public property namedCurrentequipped with agetaccessor.
Technical Constraints and Features
- Iteration Variable Mutability: By default, the
iterationVariableis read-only. Attempting to reassign a standard by-value iteration variable will result in compiler error CS1656. However, if the enumerator’sCurrentproperty returns by reference (ref T), the iteration variable can be declared with therefmodifier (e.g.,foreach (ref int item in span)). This allows direct reassignment and mutation of the underlying collection’s elements. - Collection Modification: The underlying enumerator tracks the version or state of the collection. If the collection is structurally modified (elements added, removed, or reallocated) during the
foreachexecution, the next call toMoveNext()will typically throw anInvalidOperationException. - Asynchronous Iteration: For collections implementing
IAsyncEnumerable<T>or the asynchronous enumerable pattern, theawait foreachvariant asynchronously retrieves the next element, yielding control back to the calling thread while waiting for data to materialize. The compiler expands this into a state machine utilizingIAsyncEnumerator<T>andDisposeAsync().
Master C# with Deep Grasping Methodology!Learn More





