programming4us
programming4us
WEBSITE

Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
4/1/2015 5:08:32 AM

The role of a single element operator is to add an extension method to any type that implements IEnumerable<T> and return a single element from that collection. The built-in standard query operators First, Last and ElementAt are examples of this category of operator, returning the first element, the last element, or the element at a given index position, respectively.

Understanding how Microsoft constructed the standard query operators is the first step to understanding how to write custom operators. To explore Microsoft’s approach to operator construction, let’s build our own version of the Last operator before moving onto building a more complex operator.

Building Our Own Last Operator

To learn how to build an operator that returns a single element, let’s first look at how the built-in standard query operator Last is implemented. The shortest compilable implementation for the Last operator is shown in Listing 1.

Listing 1. Shortest compilable Last operator implementation

image

Whether to Use Foreach or GetEnumerator Looping Syntax

In the Last example, I chose to manually create and use the enumerator based on the IEnumerable interface pattern. Using a foreach loop in this example would also have worked and may have made the code easier to read. There is some (not much, but some) performance improvement when using the pure enumerator pattern over the foreach style of looping. Here is an implementation using the foreach syntax:

image

Operators are often used in loops, and attention to performance should be a goal. Other developers will assume that these operators are optimized, and we shouldn’t disappoint.

The code in Listing 1 satisfies the compiler and is completely functional. It simply takes an IEnumerable<T> collection as its source, iterates to the last element, and returns that element as its result. The one error condition trapped by this implementation is the case where there are no elements in the source collection. Throwing an InvalidOperationException is the standard pattern used by Microsoft’s operator implementations, and custom operators should follow this pattern for consistency (omitting the InvalidOperationException in the previous code would cause an error in compilation because not all code paths return a value, so it is not really optional).

Another error condition to be handled is when the source collection is itself null (not empty, but uninitialized). The pattern used by Microsoft in this case is to throw an ArgumentNullException and to test the source argument for this condition at the beginning of any operator, as the following code demonstrates:

image

This implementation is fully functional and follows all of the error condition patterns that Microsoft employs in their operators. Once the error conditions are satisfied, performance improvement can be explored.

The current implementation iterates the entire source collection to get to the last element, all 100,000 of them if that is the size of the source collection. If the collection has a high element count, this could be considered a performance issue. For many collection types, the last element can be retrieved with a single statement. Many collections that implement IEnumerable<T> also implement the interface IList<T>. IList<T> collections allow access by element index position, a zero-based count from the first element. If a collection implements IList<T>, then custom operators should first exhaust that avenue of processing before using the slower IEnumerable<T> enumeration algorithm approach. The code shown in Listing 2 demonstrates how to use index position if possible (otherwise using the enumeration pattern of choice).

Listing 2. Last operator implementation with recommended error handling and performance optimizations

image

This implementation first tries to cast the source collection to the IList<T> interface; if it is successful, it uses that interface to resolve the last element in a single statement. If the collection does not implement IList<T>, the do-while enumeration pattern using the IEnumerable<T> interface is employed. The same error-handling patterns are followed, and this implementation is equivalent to the patterns used by Microsoft in its operator implementations.

Does the LINQ to Objects Provider Have Built-in Performance Optimizations?

More detail is available in the blog posting, but Table 1 covers the main points.

Table 1. LINQ to Objects Built-in Performance Optimizations

image

Throwing an exception when there are no source collection elements (an empty sequence) may not be desirable behavior when using single element return operators. It is difficult dealing with exceptions from within a query expression (nowhere to put the try-catch statement). Many operators implement a variant of these operators that return a null value or a default value you specify. The operators FirstOrDefault and LastOrDefault are examples that carry out the basic function of their partners, but instead of throwing an exception when the source sequence is empty, they return the default value of the type by calling the default keyword on the specific underlying generic type. For our Last implementation, we would add an operator called LastOrDefault, and the code for this operator would be (the only alteration from the code for the final Last operator is the operator’s name and the last line):

image

What Does the Default Keyword Return?

The default keyword solves the issue of not knowing what default value to initially assign to a generic type and returns null for reference types and zero for numeric value types. For structs, it will return each member of the struct initialized to zero or null, depending on whether they are value or reference types.

Other  
  •  PHP Tutorials : Storing Images in MySQL with PHP (part 2) - Creating the HTML, Inserting the Image into MySQL
  •  PHP Tutorials : Storing Images in MySQL with PHP (part 1) - Why store binary files in MySQL using PHP?
  •  C# Tutorial: Reading and Writing XML Files (part 2) - Reading XML Files
  •  C# Tutorial: Reading and Writing XML Files (part 1) - Writing XML Files
  •  C# 2010 and the .NET 4 Platform : Understanding the ASP.NET Profile API (part 4) - Grouping Profile Data and Persisting Custom Objects
  •  C# 2010 and the .NET 4 Platform : Understanding the ASP.NET Profile API (part 3) - Accessing Profile Data Programmatically
  •  C# 2010 and the .NET 4 Platform : Understanding the ASP.NET Profile API (part 2) - Defining a User Profile Within Web.config
  •  C# 2010 and the .NET 4 Platform : Understanding the ASP.NET Profile API (part 1) - The ASPNETDB.mdf Database
  •  C# 2010 and the .NET 4 Platform : The Role of the sessionState Element - Storing Session Data in the ASP.NET Session State Server, Storing Session Data in a Dedicated Database
  •  C# 2010 and the .NET 4 Platform : ASP.NET State Management Techniques - Understanding Cookies
  •  
    Top 10
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
    - Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
    - Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    programming4us programming4us
    programming4us
     
     
    programming4us