What's the usage of AsEnumerable

  • Gérald Barré

The extension method AsEnumerable is just an identity method. This means it always returns the same value that was used as its argument:

public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}

You may wonder what is the interest of this method. To answer this question you must understand how extensions methods work.

An extension method extends the functionality of a class from the outside (i.e. without modifying it nor extending it). Thus, these methods can only access the public visibility of the object they extend. To be clear, this is just a syntactic sugar. Without extension methods, you must call the static method as usual:

Enumerable.AsEnumerable(obj);

With extension methods, you can write:

obj.AsEnumerable();

The Enumerable does not appear in the second syntax. The compiler must therefore determine where the method AsEnumerable is defined. There are some difficulties:

  • Several extension methods can extend the same type
  • Several extension methods can extend types belonging to the same class hierarchy

If multiple extension methods are compatible, the compiler try to bind one of them using the following rules:

  • If several extension methods are found in different namespace, the ones in the current namespace take precedence over others.
  • If several extension methods are found for different types in the same hierarchy (classes or interfaces) then the one that match the type of the variable takes precedence over others.
  • If there is still a conflict, the compiler raises an error

In the following example, the compiler cannot determine the right extension method and raise an error:

static void Main()
{
    var list = new List<int>();
    // The compiler doesn't know which method to call here
    // 1. List<T>.First doesn't exist so it searches for compatible extension methods
    // 2. List<T> implements IEnumerable<T>, IList<T> and IReadOnlyList<T>
    //    So, ExtensionMethods.First(IList<T>) and ExtensionMethods.First(IReadOnlyList<T>) are compatible.
    //    Note that Enumerable.First is also compatible, but the compiler prefer extension methods in the same namespace as the call site, so this method is excluded
    // => In this case the compiler can't choose and raise an error
    list.First(); // CS0121 The call is ambiguous between the following methods or properties: 'ExtensionMethods.First<T>(IReadOnlyList<T>)' and 'ExtensionMethods.First<T>(IList<T>)'

    // Here the call is not ambiguous as the compiler know we want to use the extension method on IList<T>
    ((IList<int>)list).First();
}

static class ExtensionMethods
{
    public static T First<T>(this IReadOnlyList<T> items) => items[0];
    public static T First<T>(this IList<T> items) => items[0];
}

In this case, you can help the compiler by casting the object in the expected type: ((IList<int>)list).First(). This way the call is not ambiguous anymore as there is a method that matches exactly the type of the argument

Almost all the features of LINQ (Where, OrderBy, Select, Count, etc.) take as parameter IEnumerable<T> or IQueryable<T>. The method AsEnumerable, by simply returning an IEnumerable<T> from any object implementing this interface, makes it possible to force the call of methods of extension of IEnumerable<T> instead of IQueryable<T>. The AsEnumerable method is a way to force the compiler to use the extension method you expect.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee