IEnumerableExtensions Class
Definition
Provides operations over the non-generic IEnumerable surface - counting without enumerating where the source is already a collection, and walking heterogeneous tree structures recursively - for code that has to deal with reflection-shaped or legacy sequences.
public static class IEnumerableExtensions
- Inheritance
-
IEnumerableExtensions
- Inherited Members
Remarks
Most modern code uses IEnumerable<T>, but reflection, COM interop, ADO.NET, and older library
boundaries still hand back the non-generic IEnumerable. The companion class in
Bodu.Collections.Generic.Extensions handles the strongly typed case; this class supplies the equivalents that
work when the element type is unavailable at compile time, and complements the generic RecursiveSelect with a
dynamic-typed version for tree shapes whose nodes only share an object base.
The API surface is small and pragmatic: CountOrDefault short-circuits to
Count when the source already exposes one, falling back to enumeration
only when necessary, and RecursiveSelect walks a node graph where each node may yield further children via a
caller-supplied selector - with overloads that accept depth, index, or a controller delegate to influence traversal.
CountOrDefault avoids enumerating known collection types and otherwise performs a single eager pass. The
RecursiveSelect family is fully deferred and yields nodes in pre-order traversal. Both methods reject a
null source via ThrowHelper rather than failing mid-enumeration.
// Cheap count when the source already exposes ICollection - falls back to enumeration otherwise.
System.Collections.IEnumerable boxed = new ArrayList { 1, 2, 3, 4 };
int count = boxed.CountOrDefault(); // => 4
// Walk a heterogeneous control tree without committing to a generic type.
System.Collections.IEnumerable controls = root.GetChildren();
foreach (object node in controls.RecursiveSelect(c => ((Control)c).GetChildren()))
Console.WriteLine(node);
Methods
CountOrDefault(IEnumerable)
Gets the number of elements in a non-generic IEnumerable sequence, using collection fast-paths where possible.
public static int CountOrDefault(this IEnumerable source)
Parameters
sourceIEnumerableThe sequence to count. Must not be null.
Returns
- int
The number of elements in the sequence.
Remarks
When source implements neither ICollection nor
IReadOnlyCollection<T>, the sequence is fully enumerated to produce the count - a one-shot source
is consumed by the call.
Exceptions
- ArgumentNullException
sourceis null.
RecursiveSelect(IEnumerable, Func<object, IEnumerable<object>>, Func<object, int, int, object>)
Recursively flattens and projects a hierarchical structure, providing index and depth information to the selector.
public static IEnumerable RecursiveSelect(this IEnumerable source, Func<object, IEnumerable<object>> childSelector, Func<object, int, int, object> selector)
Parameters
sourceIEnumerableThe root sequence to traverse recursively.
childSelectorFunc<object, IEnumerable<object>>A delegate that returns child elements for a given item.
selectorFunc<object, int, int, object>A projection that receives the element, its index, and its recursion depth.
Returns
- IEnumerable
A flattened sequence of projected elements with access to structural context (index, depth).
Examples
var root = new[]
{
new Node { Name = "A", Children = { new Node { Name = "B" }, new Node { Name = "C" } } },
new Node { Name = "D" }
};
var structured = root.RecursiveSelect(n => ((Node)n).Children,
(n, i, depth) => new { ((Node)n).Name, Index = i, Depth = depth });
// Yields:
// { Name = "A", Index = 0, Depth = 0 }
// { Name = "B", Index = 1, Depth = 1 }
// { Name = "C", Index = 2, Depth = 1 }
// { Name = "D", Index = 3, Depth = 0 }
Remarks
Use when formatting output that depends on the depth of the node in the hierarchy (e.g., indentation, styling).
Exceptions
- ArgumentNullException
Thrown if
source,childSelector, orselectoris null.
RecursiveSelect(IEnumerable, Func<object, IEnumerable<object>>, Func<object, int, int, object>, Func<object, RecursiveSelectControl>)
Recursively flattens and projects a hierarchical structure, allowing control over whether to recurse into children.
public static IEnumerable RecursiveSelect(this IEnumerable source, Func<object, IEnumerable<object>> childSelector, Func<object, int, int, object> selector, Func<object, RecursiveSelectControl> recursionControl)
Parameters
sourceIEnumerableThe root sequence to traverse recursively.
childSelectorFunc<object, IEnumerable<object>>A delegate that returns child elements for a given item.
selectorFunc<object, int, int, object>A projection that receives the element, its index, and its recursion depth.
recursionControlFunc<object, RecursiveSelectControl>A delegate that determines how each element in the sequence should be handled during recursion. It returns a RecursiveSelectControl value indicating whether to yield the element, recurse into its children, skip it, or terminate the traversal entirely.
Returns
- IEnumerable
A sequence of projected elements, where children are included only if
recursionControlreturns true.
Examples
var root = new[]
{
new Node { Name = "A", Children = { new Node { Name = "B" }, new Node { Name = "C" } } },
new Node { Name = "D" }
};
var pruned = root.RecursiveSelect(
n => ((Node)n).Children,
(n, i, d) => ((Node)n).Name,
n => ((Node)n).Name == "A"
? RecursiveSelectControl.YieldAndRecurse
: RecursiveSelectControl.YieldOnly);
// Yields: "A", "B", "C", "D"
Remarks
Useful when pruning the recursion tree - e.g., limiting depth, skipping inactive branches, or filtering by condition.
Exceptions
- ArgumentNullException
Thrown if
source,childSelector,selector, orrecursionControlis null.
RecursiveSelect(IEnumerable, Func<object, IEnumerable<object>>, Func<object, int, object>)
Recursively flattens and projects a hierarchical structure, passing the index of each element to the projection.
public static IEnumerable RecursiveSelect(this IEnumerable source, Func<object, IEnumerable<object>> childSelector, Func<object, int, object> selector)
Parameters
sourceIEnumerableThe root sequence to traverse recursively.
childSelectorFunc<object, IEnumerable<object>>A delegate that returns child elements for a given item.
selectorFunc<object, int, object>A projection that receives each element and its zero-based index.
Returns
- IEnumerable
A depth-first, recursively flattened sequence with projected values using the element and its index.
Examples
var root = new[]
{
new Node { Name = "A", Children = { new Node { Name = "B" }, new Node { Name = "C" } } },
new Node { Name = "D" }
};
var labeled = root.RecursiveSelect(n => ((Node)n).Children, (n, i) => $"{i}: {((Node)n).Name}");
// Yields: "0: A", "1: B", "2: C", "3: D"
Remarks
Use this when you need both element content and positional context during recursion.
Exceptions
- ArgumentNullException
Thrown if
source,childSelector, orselectoris null.
RecursiveSelect(IEnumerable, Func<object, IEnumerable<object>>, Func<object, object>)
Recursively flattens and projects a hierarchical structure into a linear sequence using a projection selector.
public static IEnumerable RecursiveSelect(this IEnumerable source, Func<object, IEnumerable<object>> childSelector, Func<object, object> selector)
Parameters
sourceIEnumerableThe root sequence to traverse recursively.
childSelectorFunc<object, IEnumerable<object>>A delegate that returns child elements for a given item.
selectorFunc<object, object>A projection applied to each element.
Returns
- IEnumerable
A depth-first, recursively flattened sequence of projected elements.
Examples
var root = new[]
{
new Node { Name = "A", Children = { new Node { Name = "B" }, new Node { Name = "C" } } },
new Node { Name = "D" }
};
var names = root.RecursiveSelect(n => ((Node)n).Children, n => ((Node)n).Name);
// Yields: "A", "B", "C", "D"
Remarks
This overload is useful when you want to flatten and transform the hierarchy into a different shape or type.
Exceptions
- ArgumentNullException
Thrown if
source,childSelector, orselectoris null.
RecursiveSelect(IEnumerable, Func<object, IEnumerable>)
Recursively flattens a hierarchical or tree-like IEnumerable into a single linear sequence.
public static IEnumerable RecursiveSelect(this IEnumerable source, Func<object, IEnumerable> childSelector)
Parameters
sourceIEnumerableThe root sequence to traverse recursively. Each element is expected to potentially contain child elements retrievable via the
childSelectordelegate.childSelectorFunc<object, IEnumerable>A delegate that returns the child elements for a given item. This should return an IEnumerable representing the recursive children of the current element, or null if none.
Returns
- IEnumerable
A flattened IEnumerable that yields all elements in depth-first order, including their recursive children.
Examples
var root = new[]
{
new Node { Name = "A", Children = { new Node { Name = "B" }, new Node { Name = "C" } } },
new Node { Name = "D" }
};
var flattened = root.RecursiveSelect(n => ((Node)n).Children);
// Yields: A, B, C, D
Remarks
This method is useful for navigating recursive structures like directory trees, category hierarchies, or comment threads.
Execution is deferred and will only begin when the resulting sequence is enumerated.
All elements are treated as object and may need casting to their actual types.
Exceptions
- ArgumentNullException
Thrown if
sourceorchildSelectoris null.
Applies to
| Product | Versions |
|---|---|
| .NET | 8, 10 |