Question

I seem to be having trouble executing a lambda expression that I've previously assigned to a variable. Here's a small C# example program I've put together:

public class Program
{
    public static void Main(string[] args)
    {
        int[] notOrdered = { 3, 2, 5, 8, 1, 4, 7, 9, 6 };
        Print(notOrdered);
        
        IEnumerable<int> ascOrdered = Order(notOrdered, true);
        Print(ascOrdered);

        IEnumerable<int> descOrdered = Order(notOrdered, false);
        Print(descOrdered);
    }
    
    static IEnumerable<T> Order<T>(IEnumerable<T> enumerables, bool ascending)
    {
        Expression<Func<T, object>> selector = (z) => z; // simple for demo purposes; pretend it's complex
        if (ascending)
            return enumerables.OrderBy(z => selector);
        else
            return enumerables.OrderByDescending(z => selector);
    }
    
    static void Print<T>(IEnumerable<T> enumerables)
    {
        foreach(T enumerable in enumerables)
            Console.Write(enumerable.ToString() + " ");
        Console.WriteLine();
    }
}

I want it to produce this output:

3 2 5 8 1 4 7 9 6

1 2 3 4 5 6 7 8 9

9 8 7 6 5 4 3 2 1

But, confusingly, it produces this output:

3 2 5 8 1 4 7 9 6

3 2 5 8 1 4 7 9 6

3 2 5 8 1 4 7 9 6

Basically, I just want to be able to pass the same expression to the two different ordering operations without having to type it out twice, hence why I assign it to selector beforehand. I have a real-world use case where the lambda expression is really long/messy and I don't want to duplicate the mess, I'd rather just refer to a variable like I have here.

So, a) what is causing the current output? b) how can I get the output that I want?

Was it helpful?

Solution

In your code you don't use the expression.

Your code is like:

object selector = new object();
if (ascending)
    return enumerables.OrderBy(z => selector);
else
    return enumerables.OrderByDescending(z => selector);

Your code should be without expression:

Func<T, object> selector = (z) => z;
if (ascending)
    return objects.OrderBy(selector);
else
    return objects.OrderByDescending(selector);

However, if you really want an expression (for test purpose or anything else), compile it before:

Expression<Func<T, object>> selector = (z) => z;
var compiledExpression = selector.Compile();
if (ascending)
    return objects.OrderBy(compiledExpression);
else
    return objects.OrderByDescending(compiledExpression);

OTHER TIPS

a) Right now, you're ordering by an Expression<Func<T,object>> (the actual expression instance), not by the object itself. This effectively makes the ordering always order using the same object, which means the order by clauses don't change anything (all items are "equal", since they're equal to the same instance of the expression).

b) I believe you want:

static IEnumerable<T> Order<T>(IEnumerable<T> enumerables, bool ascending)
{
    if (ascending)
        return enumerables.OrderBy(z => z);
    else
        return enumerables.OrderByDescending(z => z);
}

You are not invoking selector, just comparing the instance of the expression. The correct way should be

static IEnumerable<T> Order<T>(IEnumerable<T> enumerables, bool ascending)
{
    Func<T, object> selector = (z) => z;
    if (ascending)
        return enumerables.OrderBy(z => selector(z));
    else
        return enumerables.OrderByDescending(z => selector(z));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top