与Math.Min或Math.Max短路进行比较

本文关键字:Math 比较 短路 Max Min | 更新日期: 2023-09-27 18:20:37

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

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

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

由于Math.Max(y, z())将返回至少与y一样大的值;y,则不需要评估z(),这可能需要一段时间。与Math.Min类似。

我意识到这两者都可以按照的思路重写

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

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

与Math.Min或Math.Max短路进行比较

正如其他人所指出的,编译器对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))) 

不,它不会短路,z()将始终被求值。如果你想要短路行为,你应该按照你所做的重写。

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

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

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

因此,我创建了一个微基准,分别对这两个表达式求值1000000次。对于z(),我使用了一个函数,该函数使用递归方法计算Fib(15)。以下是运行这两个程序的结果:

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

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

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

代码的求值顺序为:z(),Math.Max,x>。。。

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