Search code examples
c#linqlambdaexpression-treesiqueryable

Why does this Linq method accepts parameters of type Expression


I found this method in other post, and I pretty much understand what's going on, except for the 4th line:

public static IQueryable<T> OrderByMember<T>(this IQueryable<T> source, string memberPath)
{
    var expression = source.Expression;

    var parameter = Expression.Parameter(source.ElementType, "x");

    string[] paths = memberPath.Split('.');

    /*****************HERE*******************/

    var member = paths.Aggregate((Expression)parameter, Expression.PropertyOrField);

    /****************************************/




    var selector = Expression.Lambda(member, parameter);
    var orderByCall = Expression.Call(typeof(Queryable), "OrderBy",
        new Type[] { parameter.Type, member.Type },
        expression, Expression.Quote(selector));
    return source.Provider.CreateQuery<T>(orderByCall);
}

Why does IEnumerable<string>.Aggregate accepts an Expression and a MemberExpression?

None of the overloads of Aggregate seem to match this call

https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.aggregate?view=netframework-4.6.2

Since I don't have a clue what's hapenning there, could you please explain?


Solution

  • Why does IEnumerable.Aggregate accepts an Expression and a MemberExpression?

    The second argument passed to Aggregate is not a MemberExpression! Look carefully!

    Expression.PropertyOrField is not a property. It is a method. Since there are no brackets at the end, you are not calling it. Expression.PropertyOrField represents a method group here.

    Expression.PropertyOrField accepts an Expression and a string and returns a MemberExpression, so it can be represented by the delegate type Func<Expression, string, MemberExpression>. And the first argument is of type Expression, so you are essentially calling:

    Aggregate(Expression, Func<Expression, string, MemberExpression>)
    

    This matches the signature of this overload of Aggregate:

    public static TAccumulate Aggregate<TSource,TAccumulate> (
        this System.Collections.Generic.IEnumerable<TSource> source, 
        TAccumulate seed, 
        Func<TAccumulate,TSource,TAccumulate> func);
    

    TAccumulate is Expression and TSource is string.

    As for what this line is doing on a high-level, well, I'm sure you've figured out by now. It converts the string memberPath, which might look something like this:

    w.x.y.z
    

    into an Expression object that represents that expression.