Sep 26, 2009

IEnumberable, IQueryable , Lambda expression - part1

When we type the following code

IEnumerable<int> intEnumerable = null;
var q1 = intEnumerable.Where( x => x > 10);

we know that Where method is not part of the IEnumberable<t> interface or IEnumberable interface, it comes from extension method of Enumerable, which is static class and it has no inheritance relation With IEnumerable or IEnumberable<t>. The power of Linq-To-Object does not come from IEnumberable or IEnumberable<t> or its implemenation, it comes from the extension method. Let's take a look what does the extension method do? Using Reflector we get the following source code.


public static class Enumerable
{
public static IEnumerable<tsource> Where<tsource>(this IEnumerable<tsource> source, Func<TSource, bool> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    if (source is Iterator<tsource>)
    {
        return ((Iterator<tsource>) source).Where(predicate);
    }
    if (source is TSource[])
    {
        return new WhereArrayIterator<tsource>((TSource[]) source, predicate);
    }
    if (source is List<tsource>)
    {
        return new WhereListIterator<tsource>((List<tsource>) source, predicate);
    }
    return new WhereEnumerableIterator<tsource>(source, predicate);
}
}

We can see there , the delegate passed in to the method is the code that does the filtering.

IQueryable<t> inherit from IEnumerable. But what extra value does the IQueryable bring. Let's take a look of the following code. and the code it generated by c# compiler.

public interface IQueryable<t> : IEnumerable<t>, IQueryable, IEnumerable
{}

public interface IQueryable : IEnumerable
{
        Type ElementType { get; }
        Expression Expression { get; }
        IQueryProvider Provider { get; }
}

It does not tell too much? Let's move on an querable example and decomplie to see what it does.

IQueryable<int> intQuerable = null;
var q2 = intQuerable.Where(x => x > 10);

// decomplied by reflector
ParameterExpression CS$0$0000;
IQueryable<int> q2 = intQuerable.Where<int>(Expression.Lambda<Func<int, bool>>(Expression.GreaterThan(CS$0$0000 = Expression.Parameter(typeof(int), "x"), Expression.Constant(10, typeof(int))), new ParameterExpression[] { CS$0$0000 }));

From this example, we can see that the Lamda Expression is not converted to a delegate, but to an expression tree. But why the extension method Enumerable.Where(IEnumerable Where(this IEnumerable source, Func predicate) is not used? It turns out that, the c# compiler pick a more a suitable extension from Queryable. Here is the code from Reflector.

public static IQueryable<tsource> Where<tsource>(this IQueryable<tsource> source, Expression<Func<TSource, bool>> predicate)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    if (predicate == null)
    {
        throw Error.ArgumentNull("predicate");
    }
    return source.Provider.CreateQuery<tsource>(Expression.Call(null, ((MethodInfo) MethodBase.GetCurrentMethod()).MakeGenericMethod(new Type[] { typeof(TSource) }), new Expression[] { source.Expression, Expression.Quote(predicate) }));
}

Unlike the "Where" method of the "Enumerable", this method does not have a delegate to do the filtering. And also the expression can not do the filtering either, it is the IQuerable.Provider which does the filtering. The provider takes the expression tree and does filtering later by converting expression tree to provider specific algorithm like TSQL.


IEumberable<t> is very easy to implement, in fact all the collection they are IEnumberable<T>. Iterator makes it even easier. So there is not such thing as implementing a IEnumberableProvider, because the delegate does the query. But to implement IQueryable is more difficult, because expression does not query. It is IQueryProvider does the job. You need to implement IQuerableProvider


public interface IQueryProvider
{
    IQueryable CreateQuery(Expression expression);
    IQueryable<telement> CreateQuery<telement>(Expression expression);
    object Execute(Expression expression);
    TResult Execute<tresult>(Expression expression);
}