我在进行基准测试时遇到了这个问题。

bool b;
MyStruct s;
for (int i = 0; i < 10000000; i++)
{
    b = (object)s == null;
}


bool b;
MyStruct? s = null;
for (int i = 0; i < 10000000; i++)
{
    b = (object)s == null;
}



我可以理解此结果,因为将可空结构强制转换为object给了我该结构的盒装类型。 但是为什么不将struct s转换为object进行空比较(如第一种方法一样)却能获得相同的性能呢? 是不是编译器正在优化始终返回false的调用,因为结构不能为null?

最佳答案

是的,编译器正在优化它。

它知道结构永远不能为空,因此将其转换为对象的结果永远不能为空——因此它只会在第一个示例中将 b 设置为 false。事实上,如果你使用 Resharper,它会警告你表达式总是错误的。

对于第二个当然,一个可为空的可以为空,因此它必须进行检查。

(您也可以使用 Reflector 检查编译器生成的 IL 代码以验证这一点。)

原始测试代码不好,因为编译器知道可空结构始终为空,因此也会优化掉该循环。不仅如此,在发布版本中,编译器意识到没有使用 b 并优化了整个循环。

为了防止这种情况,并显示在更真实的代码中会发生什么,请像这样测试:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            bool b = true;
            MyStruct? s1 = getNullableStruct();
            Stopwatch sw = Stopwatch.StartNew();

            for (int i = 0; i < 10000000; i++)
            {
                b &= (object)s1 == null; // Note: Redundant cast to object.
            }

            Console.WriteLine(sw.Elapsed);

            MyStruct s2 = getStruct();
            sw.Restart();

            for (int i = 0; i < 10000000; i++)
            {
                b &= (object)s2 == null;
            }

            Console.WriteLine(sw.Elapsed);
        }

        private static MyStruct? getNullableStruct()
        {
            return null;
        }

        private static MyStruct getStruct()
        {
            return new MyStruct();
        }
    }

    public struct MyStruct {}
}

10-07 20:10
查看更多