12

当与两个数字/函数的最小值或最大值进行比较时,如果第一个的情况为真并且第二个的情况为真,C# 是否会短路?这些情况的具体例子是

if(x < Math.Max(y, z()))

if(x > Math.Min(y, z()))

因为Math.Max(y, z())将返回一个至少与 y 一样大的值,如果 x < y 则不需要计算 z(),这可能需要一段时间。与 类似的情况Math.Min

我意识到这些都可以按照以下方式重写

if(x < y || x < z())

为了短路,但我认为在不重写的情况下比较是什么更清楚。这是短路吗?

4

5 回答 5

18

正如其他人指出的那样,编译器对 Min 或 Max 的语义一无所知,这将允许它打破在调用方法之前评估参数的规则。

如果你想自己写,你可以很容易地做到这一点:

static bool LazyLessThan(int x, int y, Func<int> z)
{
    return x < y || x < z();
}

然后调用它

if (LazyLessThan(x, y, z))

或者

if (LazyLessThan(x, y, ()=>z()))

或者就此而言:

static bool LazyRelation<T>(T x, T y, Func<T> z, Func<T, T, bool> relation)
{
    return relation(x, y) || relation(x, z());
}
...
if (LazyRelation(x, y, ()=>z, (a,b)=> a < b))) 
于 2012-01-18T19:27:19.810 回答
10

不,它不会短路,并且 z() 将始终被评估。如果你想要短路行为,你应该像你所做的那样重写。

于 2012-01-18T19:13:07.643 回答
5

Math.Min()Math.Max()其他方法一样。必须对它们进行评估才能返回将用作比较中的第二个参数的值。如果您想要短路,那么您将不得不使用||您演示的运算符编写条件。

于 2012-01-18T19:14:07.483 回答
3

(没有什么特别新的东西要添加,但我想我会分享我在上面运行的测试的结果。)

Math.Max() 可以很容易地被 CLR 的即时编译器内联,从那里我很好奇它是否可以进一步优化代码以使其短路。

所以我创建了一个微基准,对这两个表达式分别评估 1,000,000 次。对于 z(),我使用了一个使用递归方法计算 Fib(15) 的函数。下面是运行这两个的结果:

x < Math.Max(y, z()) :   8097 ms
x < y || x < z()     :     29 ms

我猜 CLR 不会以任何阻止方法调用执行的方式转换代码,因为它不知道(也不检查)例程是否有任何副作用。

于 2012-01-18T20:34:46.830 回答
2

不,它不会短路,至少在 C# 编译器级别。Math.Min或者Math.Max是两个普通的静态方法调用,编译器不会在这个意义上进行优化。

代码的评估顺序将是:z()、Math.Max、x > ...

如果您真的想确定,请查看 IL 代码。

于 2012-01-18T19:13:45.787 回答