What's the usage of AsEnumerable

 
 
  • Gérald Barré

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

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

You might wonder why this method is useful. To answer that, you need to understand how extension methods work.

An extension method adds functionality to a class from the outside (i.e., without modifying or extending it). These methods can therefore only access the public members of the object they extend. In short, extension methods are syntactic sugar. Without them, you would call the static method directly:

C#
Enumerable.AsEnumerable(obj);

With extension methods, you can write:

C#
obj.AsEnumerable();

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

  • 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 tries to bind one of them using the following rules:

  • If several extension methods are found in a 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 matches 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 raises an error:

C#
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 to the expected type: ((IList<int>)list).First(). This makes the call unambiguous, as there is now a method that exactly matches the type of the argument.

Most LINQ features (Where, OrderBy, Select, Count, etc.) take IEnumerable<T> or IQueryable<T> as a parameter. By returning an IEnumerable<T> from any object that implements the interface, AsEnumerable lets you force the use of IEnumerable<T> extension methods instead of IQueryable<T> ones. It is a simple way to tell the compiler which extension method you want to use.

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

Follow me:
Enjoy this blog?