分配一个 lambda 表达式会导致它以后不被执行吗?

5

我似乎遇到了一个问题,就是在执行一个之前分配给变量的lambda表达式时出现了困难。以下是我编写的一个小型C#示例程序:

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();
    }
}

我希望它输出如下结果:
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
但是,令人困惑的是,它却输出了这个:
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
基本上,我只想能够将同一个表达式传递给两个不同的排序操作,而不必输入两次。因此我先将其赋值给"selector"。在我的实际应用中,lambda表达式非常冗长/混乱,我不想重复这个麻烦,我宁愿像这里一样引用一个变量。
那么,a)是什么原因导致当前输出? b)我如何得到我想要的输出?

1
我不清楚你为什么要使用 Expression<Func<T, object>> selector = (z) => z; 而不是只用 Func<T, object> selector = (z) => z;;如果你将其更改为 Func,并使用 OrderBy(selector),它是否按预期工作? - Lorenzo Dematté
冗长的代码会让人崩溃。你正在按照函数对象进行排序,而不是它返回的结果。如果你这样写 Print(notOrdered.OrderBy(i=>i)); 它就能正常工作了。 - Sten Petrov
代码甚至无法编译(我也猜不出它应该是什么)。制作可工作的代码将使您的意图更清晰。 - usr
@usr 代码编译通过了,伙计。http://rextester.com/WNPU91371 - XåpplI'-I0llwlg'I -
啊!它编译通过了。我无法理解 z => selector 这段代码。顺便说一下,这就是你的 bug :) - usr
3个回答

9

您的代码中没有使用该表达式。

您的代码类似于:

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

你的代码应该没有表达式:

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

然而,如果你真的需要一个表达式(无论是为了测试目的还是其他任何原因),请先编译它:

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

7

a) 现在,你正在通过 Expression<Func<T,object>>(实际表达式实例)进行排序,而不是通过对象本身进行排序。这有效地使得排序总是使用同一个对象进行排序,这意味着排序子句不会改变任何东西(所有项目都“相等”,因为它们都等于表达式的同一实例)。

b) 我认为你想要:

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

1
你没有调用selector,只是比较表达式的实例。正确的方式应该是:
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));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接