Using Extension Methods with LINQ (
Page 1 of 2 )
Everyone knows that LINQ is for queries, but you can also use it for interpretation of data, which makes LINQ far more powerful than you may have thought.
I’ve been working with LINQ for a while now and it’s an amazing technology. However, like many amazing technologies, people will probably approach it with preconceived notions and miss opportunities to use LINQ for some special tasks. Extension methods could be one of the situations where people miss an opportunity because they tend not to follow the same rut that other development aids have followed in the past. You can combine extension methods with LINQ to create extremely flexible code that performs tasks in far less space than other coding methods allow. In fact, most of my experience with LINQ has been of doing a lot more with less code and extension methods are simply one good example of that behavior.
Understanding Extension Methods
Extension methods are an important new feature for LINQ developers because this feature helps you map keywords to methods when you want to override the default expression handling functionality. An extension method provides a means of adding new methods to an existing class. For LINQ developers, the use of extension methods begins when you start working with LINQ itself. For example, you may decide that you don’t like the way where works, so you add a new where to your code that provides the functionality you want. This feature can significantly change the way LINQ works and make your coding significantly easier, while providing a customized approach to performing queries.
Using extension methods to modify LINQ directly is probably one feature that most developers will remember with ease because it’s so interesting. However, it's important to remember that the relationship between extension methods and LINQ need not always be one where LINQ depends on extension methods for support. You can also use LINQ to extend common types. For example, you may want to count the special characters in a string. Using standard C# code, you’d need to write a loop and a rather large switch statement. To add the required functionality, you’d also have to write your own version of String that wouldn’t appear as part of the original String class. When working with LINQ, you can create a small extension to perform the same task in a way that would look natural to any developer using it.
Extension methods are going to be popular for a number of reasons. Obviously, flexibility is one of them. However, there are two essential reasons to use extension methods in your code.
- You can add new functionality without recompiling the existing type.
- You can change the behavior of an existing .NET Framework type without subclassing it.
Modifying LINQ Behavior
Basic LINQ queries use operators that define the search criteria you want to use. These operators perform specific tasks in specific ways. You might wonder what happens if the functionality that LINQ provides isn't sufficient for a particular requirement. For example, you may find that you want to tweak how the where keyword works. It turns out that each of the keywords has a matching method that you can override. In short, you can change the behavior of LINQ to meet specific needs.
The example in this section demonstrates a simple technique for modifying the behavior of the where keyword. Let’s say you begin with a basic query like the one shown in Listing 1.
Listing 1: Filtering a Query by Length
private void btnTest_Click(object sender, EventArgs e)
{
// Create an array as a data source.
String[] QueryString =
{ "One", "Two", "Three", "Four", "Five" };
// Define the query.
var ThisQuery =
from StringValue in QueryString
where StringValue.Length > 3
select StringValue + "\r\n";
// Display the result.
foreach (var ThisValue in ThisQuery)
txtResult.Text = txtResult.Text + ThisValue;
}
In this case, the output of the where part of the query limits the query results to Three, Four, and Five. However, you can modify this basic example by creating your own version of where. All you need is a new class with a specially named method. Listing 2 shows such an example.
Listing 2: Modifying the Behavior of where
// The extension method must appear in a public static
// class.
public static class MyWhere
{
// Define the new version of where.
// The definition must match precisely.
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
// Obtain the number of elements in the source
// array.
int ThisCount = source.Count<TSource>();
// Create a new array of the correct type.
TSource[] Results = new TSource[ThisCount];
// Initialize the array.
Results.Initialize();
// Create a counter to track the current results
// element.
int ArrayCount = 0;
// Process the source array.
foreach (TSource ThisValue in source)
{
// When the source array element meets the
// required value...
if (ThisValue.ToString().Length > 3)
{
// add it to the results array and increment
// the array counter.
Results[ArrayCount] = ThisValue;
ArrayCount++;
}
}
// Return the matching elements.
return Results;
}
}
In this case, LINQ ignores the where part of the query shown in Listing 1 and uses the special class in Listing 2 instead. The output is the same as before, but you could do anything in this new class. Try it out with the debugger and you'll find that the moment the debugger gets to the query, it creates the query and then moves to the extension class shown in Listing 2. The application recognizes this custom class as containing a new version of the Where operator.
The custom version isn't elegant, but it does the job. It begins by creating a new array that matches the data type of the source data. The code initializes the array and a counter used to track the current Results array element.
The foreach loop processes the source array. Whenever the source array contains a value that matches the criteria, it places the value in the Results array. When the processing is complete, the code passes the array back to the caller.
This example demonstrates an interesting aspect of LINQ. The processing of the source array occurs before the code calls the foreach loop in the main code block. Just in case you wondered how LINQ creates the query, you now have a better idea of what happens. The processing occurs immediately, during the creation of the query.