The LINQ Principle
Written by Mike James   
Thursday, 25 June 2020
Article Index
The LINQ Principle
IEnumerable
Generic enumeration
Raw LINQ
More with LINQ

Banner

Generic enumeration

It is generally better to use a generic form of the interfaces so add to the start of the program:

using System.Collections.Generic;

The generic form of the IEnumerator interface inherits from the non-generic IEnumerator interface.

Note that when an interface inherits from another interface, for example IA:IB then when you add IA to a class it’s exactly the same as writing :IA,IB and you have to implement the methods of IA and IB.

This sounds bad as it now looks as if we are going to have to implement both the non-generic and generic forms of the interface. Fortunately the generic interface only extends the non-generic interface by one method.

The generic form of the IEnumerator interface defines a single generic version of Current – after all this is the only method that needs to use the data type. To complicate things a little it also inherits the Dispose method  from IDisposable but we can ignore this at the moment by adding a null implementation.

The generic form of IEnumerable interface simply adds a generic form of GetEnumerator. So to make our class use the generic Interfaces all we have to do is change its definition to:

class TestCollection : IEnumerable<int>,
IEnumerator

and add to it a second generic GetEnumerator method:

IEnumerator<int> 
IEnumerable<int>.GetEnumerator()
{
return new
TestCollectionEnnumerator(this);
}

The enumerator class also has to implement the generic interface and has to have two new methods added to it:

class TestCollectionEnnumerator :
IEnumerator<int>
{
…rest of class definition

New methods:

int IEnumerator<int>.Current{
get
{
if (loc > -1)
return m_Instance.rnd.Next(500);
else
return -1;
}
}

void IDisposable.Dispose()
{
}

Notice that you have to keep the existing non-generic implementations and that the new generic method is almost identical to its non-generic version apart from the use of the int data type. Now you can write:

foreach (int o in col)
{
 MessageBox.Show(o.ToString());
}

LINQ and extension methods

Now we have a simple generic IEnumerable class we can start to use LINQ with it – yes it really is this simple.

What the LINQ system does it to add extension methods to the IEnumerable generic interface – I’ll come back to what an extension method is after we have seen them in action. For the moment just accept the fact that there are a large number of additional methods available to any class that implemented the IEnumerable generic interface and most of these return an IEnumerable object  - which is a more important observation than you might think.

To get started let’s look at the Where extension method. If you look at its definition you will discover that it’s:

public static IEnumerable<T> Where<T>(
      this,
 IEnumerable<T> source,
      Func<T, bool> predicate
)

Ignore the “this” for the moment because it’s part of every extension method.

The first parameter, source, is an IEnumerable that will be “scanned” for all of the entities that the predicate returns true for. 

The predicate is just a generic delegate that “wraps” a function that accepts a single input parameter of the first specified type and returns a result of the second specified type.

In this case predicate can be seen to accept a single parameter of type T and return a Boolean result. If we are going to use Where the first thing we need is a suitable predicate.  This can be created in many ways but to keep the explanation simple let’s do it the old-fashioned but rather long-winded way of first defining a suitable function and then wrapping it in a delegate.

First define the function:

bool MyTest(int i)
{
return i > 250;
}

This simply tests to see if the value is greater than 25 and returns true if so and false otherwise. To make use of this we have to first wrap it in a delegate:

Func<int, bool> MyDelegate = 
new Func<int, bool>(MyTest);

Next we create the data collection as before:

TestCollection col = 
new TestCollection(5);

And finally use the Where method with the delegate we have defined:

IEnumerable<int> q = 
col.Where<int>(MyDelegate);

If you try this out nothing happens.

No really nothing happens… the operation of enumerating and extracting the entities smaller than 250 is only performed when it is required.LINQ queries are nearly alway lazy evaluated.

In practice it is triggered by calling the generic GetEnumerator method, usually within a foreach loop.

For example to see the results of the query:

foreach (int o in q)
{
MessageBox.Show(o.ToString());
}

To emphasise - the results of the query are only stored in q when we start the foreach loop.

 



Last Updated ( Thursday, 25 June 2020 )