EnumerableExtensions
Provides powerful LINQ-style operations and utilities for IEnumerable collections.
Overview
The EnumerableExtensions class offers enhanced collection operations including chunking, batch processing, conditional operations, and advanced LINQ extensions.
API Reference
Batch Processing
Chunking Operations
ChunkBy<T>(IEnumerable<T> source, int size)
Splits collection into chunks of specified size.
var numbers = Enumerable.Range(1, 10);
var chunks = numbers.ChunkBy(3);
// Result: [[1,2,3], [4,5,6], [7,8,9], [10]]
foreach (var chunk in chunks)
{
Console.WriteLine($"Chunk: [{string.Join(", ", chunk)}]");
}
Batch Processing
ProcessInBatches<T>(IEnumerable<T> source, int batchSize, Action<IEnumerable<T>> processor)
Processes collection in batches with specified action.
var largeDataset = GetLargeDataset();
// Process 100 items at a time
largeDataset.ProcessInBatches(100, batch =>
{
// Process batch efficiently
var results = ProcessBatchInDatabase(batch);
logger.LogInformation($"Processed {batch.Count()} items");
});
Conditional Operations
WhereIf Extension
WhereIf<T>(IEnumerable<T> source, bool condition, Func<T, bool> predicate)
Applies filter only if condition is true.
var users = GetUsers();
var includeInactive = GetUserPreference();
var filteredUsers = users
.WhereIf(!includeInactive, u => u.IsActive)
.WhereIf(hasAdminRights, u => u.Department == "IT");
SelectIf Extension
SelectIf<T, TResult>(IEnumerable<T> source, bool condition, Func<T, TResult> selector, Func<T, TResult> alternativeSelector)
Conditional selection with fallback.
var users = GetUsers();
var showFullName = GetDisplayPreference();
var displayNames = users.SelectIf(
showFullName,
u => $"{u.FirstName} {u.LastName}",
u => u.FirstName
);
Advanced LINQ Operations
Distinct Operations
DistinctBy<T, TKey>(IEnumerable<T> source, Func<T, TKey> keySelector)
Returns distinct elements by specified key.
var users = GetUsers();
var uniqueByEmail = users.DistinctBy(u => u.Email);
var uniqueByDepartment = users.DistinctBy(u => u.Department);
Safety Extensions
EmptyIfNull<T>(IEnumerable<T>? source)
Returns empty collection if source is null.
IEnumerable<string>? potentiallyNull = GetOptionalData();
var safeEnumerable = potentiallyNull.EmptyIfNull();
// Safe to enumerate without null checks
foreach (var item in safeEnumerable)
{
ProcessItem(item);
}
IsNullOrEmpty<T>(IEnumerable<T>? source)
Checks if collection is null or empty.
var items = GetItems();
if (items.IsNullOrEmpty())
{
DisplayNoItemsMessage();
return;
}
ProcessItems(items);
Aggregation Operations
Statistical Functions
MaxBy<T, TComparable>(IEnumerable<T> source, Func<T, TComparable> selector)
Finds element with maximum value by selector.
var employees = GetEmployees();
var highestPaid = employees.MaxBy(e => e.Salary);
var oldestEmployee = employees.MaxBy(e => e.Age);
MinBy<T, TComparable>(IEnumerable<T> source, Func<T, TComparable> selector)
Finds element with minimum value by selector.
var products = GetProducts();
var cheapestProduct = products.MinBy(p => p.Price);
var newestProduct = products.MinBy(p => p.CreatedDate);
Performance Considerations
Deferred Execution
- Most operations use deferred execution (lazy evaluation)
- Chain operations efficiently without intermediate collections
- Materialize with
.ToList()or.ToArray()when needed
Memory Efficiency
// ✅ Efficient - deferred execution
var result = largeDataset
.Where(x => x.IsActive)
.Select(x => x.Name)
.Take(10);
// ❌ Inefficient - immediate materialization
var result = largeDataset
.Where(x => x.IsActive).ToList() // Unnecessary materialization
.Select(x => x.Name).ToList() // Another unnecessary materialization
.Take(10);
Thread Safety
- Extension methods are thread-safe (no shared state)
- Be cautious with underlying collections that may not be thread-safe
- Consider using concurrent collections for parallel processing
Best Practices
- Use Deferred Execution: Chain operations before materializing
- Handle Null Collections: Use
EmptyIfNull()for safety - Optimize Batch Sizes: Choose appropriate batch sizes for your data
- Validate Input: Check for null or empty collections when appropriate
- Consider Memory Usage: Be mindful of materializing large collections
- Use Appropriate Extensions: Choose the right extension for your use case