Here are three different reasons:
An example for reason #1 is Bart De Smet’s ForEach operator. While you are on Bart’s blog, you can read about the pros and cons of a ForEach in his comments.
An example for reason #2 would be a custom join operator. Let’s say we are joining an object collection of employees to an object collection of departments.
var employeeAndDepartments = employees.Join(departments, employee => employee.DepmartmentID, department => department.ID, (employee, department) => new { Employee = employee, Department = department });
The Join operator with extension methods is a little unwieldy. You need three lambda expressions: one to specify the employee key, one to specify the department key, and one to specify the result. To make the query itself a bit more readable you could define a custom Join operator that knows how to join employees and departments.
public static IEnumerable<EmployeeDepartmentDTO> Join( this IEnumerable<Employee> employees, IEnumerable<Department> departments) { return employees.Join(departments, employee => employee.DepmartmentID, department => department.ID, (employee, department) => new EmployeeDepartmentDTO { Employee = employee, Department = department }); }
Not pretty, but it drastically cleans up code in other places:
var employeeAndDepartments = employees.Join(departments);
Reason #3 is performance. Generally speaking, you'll write an operator for performance when you know something that LINQ doesn't know.A good example is in Aaron Erickson’s i40 (Indexed LINQ) library. i40 features an IndexableCollection type that can drastically increase the performance of LINQ to Object queries (think table scan versus index seek). Imagine having a huge number of objects in memory and you commonly query to find just one.
var subject = subjects.Where(subject => subject.ID == 42)
.Single();
With i40 you can create an index on the ID property.
var subjects = /* ... */ .ToIndexableCollection() .CreateIndexFor(subject => subject.ID);
/* ... */
var subject = subjects.Where(subject => subject.ID == 42)
.Single();
If you are using the i40 namespace, you’ll get a special i40 Where operator that takes advantage of the indexes built into the IndexableCollection.
//extend the where when we are working with indexable collections! public static IEnumerable<T> Where<T> ( this IndexableCollection<T> sourceCollection, Expression<Func<T, bool>> expr ) { // ... source from IndexableCollectionExtension.cs }
What custom operators have you made?