Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assigning a lambda expression causes it to not be executed later?

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?

like image 769
XåpplI'-I0llwlg'I - Avatar asked Feb 01 '13 21:02

XåpplI'-I0llwlg'I -


3 Answers

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);
like image 180
Cédric Bignon Avatar answered Sep 21 '22 16:09

Cédric Bignon


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);
}
like image 22
Reed Copsey Avatar answered Sep 22 '22 16:09

Reed Copsey


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));
}
like image 45
I4V Avatar answered Sep 19 '22 16:09

I4V