Table of Contents

IEnumerableExtensions Class

Definition

Namespace
Bodu.Collections.Extensions
Assembly
Bodu.Core.dll
Package
Bodu.Core 1.0.1
Source
IEnumerableExtensions.CountOrDefault.cs

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

source IEnumerable

The 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

source is 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

source IEnumerable

The root sequence to traverse recursively.

childSelector Func<object, IEnumerable<object>>

A delegate that returns child elements for a given item.

selector Func<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, or selector is 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

source IEnumerable

The root sequence to traverse recursively.

childSelector Func<object, IEnumerable<object>>

A delegate that returns child elements for a given item.

selector Func<object, int, int, object>

A projection that receives the element, its index, and its recursion depth.

recursionControl Func<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 recursionControl returns 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, or recursionControl is 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

source IEnumerable

The root sequence to traverse recursively.

childSelector Func<object, IEnumerable<object>>

A delegate that returns child elements for a given item.

selector Func<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, or selector is 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

source IEnumerable

The root sequence to traverse recursively.

childSelector Func<object, IEnumerable<object>>

A delegate that returns child elements for a given item.

selector Func<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, or selector is 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

source IEnumerable

The root sequence to traverse recursively. Each element is expected to potentially contain child elements retrievable via the childSelector delegate.

childSelector Func<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 source or childSelector is null.

Applies to

ProductVersions
.NET8, 10