programming4us
programming4us
WEBSITE

Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement 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:12:17 AM

Building the RandomElement Operator

To demonstrate how to build a custom operator that returns a single element from a source sequence, let’s look at an operator that returns an element at random. There are often times when picking a candidate at random from a collection is necessary, either for test data or deriving a random sample from a customer list to provide specific advertising or business offers. This new operator will be called RandomElement and will return one element at random from a given sequence (pseudo-random to be exact—it can be difficult to get the randomness you need for a small collection or when retrieving more than one in a tight loop).

The RandomElement operator has the following requirements:

• Take an input source IEnumerable<T> sequence and return one element at random.

• Allow control over the random number generator by optionally accepting a seed value.

• Throw an ArgumentNullException if the source sequence is null.

• Throw an InvalidOperationException if the source sequence is empty.

As seen when implementing the Last operator, although it is certain that sequences you extend implement the IEnumerable<T> interface, many sequences also implement the IList<T> and ICollection<T> interfaces, which can be used to improve performance. Any collection type that implements IList allows elements to be accessed by index position (for example, list[2] will return the third element), and any collection implementing ICollection exposes a Count property that immediately retrieves element count. These performance enhancements should be used if possible.

The code shown in Listing 3 uses the built-in .NET Framework random number generator to find an index position that is within a sequence’s bounds and returns that element. It first tries to use the IList indexer to retrieve the element (because it is faster), and if that interface isn’t supported by the collection, this operator uses a slower enumeration looping pattern (completely valid code, just slower).

Optional Parameters in C# 4.0

Listing 3 demonstrates the use of optional parameters in the method declaration.

Listing 3. Sample RandomElement operator returns an element at random from a source sequence and takes an optional seed value for the random number generator

image

image

Although it is completely up to the implementer to decide behavior for empty and null source collections, following the same pattern used for the standard query operators is highly recommended, as it makes consuming your custom operators the same as the other LINQ operators. The general conventions are as follows:

  1. If the source collection being extended is null, throw an ArgumentNullException.
  2. If the source collection is empty, throw an InvalidOperationException.
  3. Provide a variation of the operator with the suffix of “OrDefault” that doesn’t raise the InvalidOperationException, but returns the value default(T) instead.

Listing 4 shows the basic unit tests written to confirm correct behavior of the RandomElement operator. (I used NUnit—see www.nunit.org—but any unit testing framework would have worked.) The basic test cases for specific input sequence types that should be confirmed at a minimum are as follows:

• The source sequence is null.

• The source sequence has no elements (is empty).

• The source sequence implements IList.

• The source sequence implements IEnumerable<T>.

These tests, shown in Listing 4, are the minimum required for the RandomElement operator, and lightly confirm both the IList<T> and IEnumerable implementations with a single element source and a two-element source. Pay attention to boundary conditions when writing these tests; my first attempt at coding this operator never returned the last element—the unit tests allowed me to find and correct this error quickly.

Iterating Over Input Sequences Multiple Times

The unit tests shown here, in some cases, iterate over the same input sequence multiple times. This should be avoided unless you have no choice but to avoid some operators failing. In this case, we know the operator and the tests we are writing, but it is generally a poor pattern.

Listing 4. Basic set of unit tests for the RandomElement operator—these tests are written for the NUnit testing framework (www.nunit.org); more tests are included in the sample code.

image

image

image

image

The code for our RandomElement operator in Listing 3 satisfies the requirements listed earlier; however, there is one more requirement that will make this single element operator conform to the patterns common in the standard query operators. We need to add a variation called RandomElementOrDefault to cater to empty source sequences without throwing an exception. This is necessary to allow the single element operators to be used on sequences that might ordinarily be empty, and rather than throwing an exception, the operator should return null for reference types or zero for numeric value types.

To add this variation of our new operator, I simply cut and pasted the code from Listing 3, changed its name to RandomElementOrDefault, and then altered the empty source check to return default(T); rather than throw new InvalidOperationException. The remaining part of the operator is unchanged (although good coding practice would have you refactor out the common code into a separate method). The additional operator begins with the following code:

image

Following are a few tips for developing new single element operators:

• Remember to test and make use of any performance efficiencies offered by collections that implement IList<T> and ICollection interfaces in addition to IEnumerable<T> if present on the source collection. Make certain to support the IEnumerable<T> sequences first before looking for performance optimizations.

• Implement an additional variation of these operators (suffixed with OrDefault) that returns a legal default value for a given type, rather than throw an InvalidOperationException (change the throw new InvalidOperationException to return default(T);)

• Keep your extension methods in a consistent namespace so that consumers of your operators only need to add a single namespace using clause to get access to your custom operators.

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