C# 双问号 ('??') vs if 在分配相同的变量时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11412639/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 17:46:12  来源:igfitidea点击:

Double question marks ('??') vs if when assigning same var

c#compiler-optimizationnull-coalescing-operator

提问by Lockszmith

Referring to the following SE answer.

参考以下SE 回答

When writing

写作时

A = A ?? B;

it is the same as

它与

if( null != A )
    A = A;
else
    A = B;

Does that mean that

这是否意味着

if( null == A ) A = B;

would be preferred, performance wise?

将是首选,性能明智?

Or can I assume that the compiler optimizes the code when the same object is in the ??notation?

或者我可以假设编译器在符号中包含相同的对象时优化了代码??

采纳答案by VCD

Although performance for ??is negligible, the side effect sometimes may not be negligible. Consider the following program.

虽然性能??可以忽略不计,但副作用有时也不能忽略。考虑以下程序。

using System;
using System.Diagnostics;
using System.Threading;

namespace TestProject
{
    class Program
    {
        private string str = "xxxxxxxxxxxxxxx";
        public string Str
        {
            get
            {
                return str;
            }
            set
            {
                if (str != value)
                {
                    str = value;
                }
                // Do some work which take 1 second
                Thread.Sleep(1000);
            }
        }

        static void Main(string[] args)
        {
            var p = new Program();

            var iterations = 10;

            var sw = new Stopwatch();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                if (p.Str == null)
                {
                    p.Str = "yyyy";
                }
            }
            sw.Stop();
            var first = sw.Elapsed;

            sw.Reset();
            for (int i = 0; i < iterations; i++)
            {
                if (i == 1) sw.Start();
                p.Str = p.Str ?? "yyyy";
            }
            sw.Stop();
            var second = sw.Elapsed;

            Console.WriteLine(first);
            Console.WriteLine(second);

            Console.Write("Ratio: ");
            Console.WriteLine(second.TotalMilliseconds / first.TotalMilliseconds);
            Console.ReadLine();
        }

    }
}

Run result on my PC.

在我的电脑上运行结果。

00:00:00.0000015
00:00:08.9995480
Ratio: 5999698.66666667

Because there is an extra assignment using ??, and the performance of the assignment sometimes might not guaranteed. This might lead to a performance issue.

因为使用 有一个额外的赋值??,并且赋值的性能有时可能无法保证。这可能会导致性能问题。

I would rather use if( null == A ) A = B;instead of A = A ?? B;.

我宁愿使用if( null == A ) A = B;而不是A = A ?? B;.

回答by Adam Houldsworth

Don't worry about the performance, it will be negligible.

不要担心性能,它可以忽略不计。

If you are curious about it, write some code to test the performance using Stopwatchand see. I suspect you'll need to do a few million iterations to start seeing differences though.

如果您对此感到好奇,请编写一些代码来测试使用Stopwatch并查看性能。我怀疑您需要进行几百万次迭代才能开始看到差异。

You can also never assume about the implementation of things, they are liable to change in future - invalidating your assumptions.

您也永远不能假设事情的实施,它们将来很可能会发生变化-使您的假设无效。

My assumption is the performance difference is likely very, very small. I'd go for the null coalescing operator for readability personally, it is nice and condense and conveys the point well enough. I sometimes do it for lazy-load checking:

我的假设是性能差异可能非常非常小。我个人会选择空合并运算符以提高可读性,它很好,很简洁,并且能很好地传达这一点。我有时会这样做以进行延迟加载检查:

_lazyItem = _lazyItem ?? new LazyItem();

回答by OnResolve

My advice would be to inspect the IL (intermediate language) and compare the different results. You can then see exactly what each boils down to and decide what is more optimized. But as Adam said in his comment, you're most likely best to focus on readability/maintainability over performance in something so small.

我的建议是检查 IL(中间语言)并比较不同的结果。然后,您可以准确地查看每个归结为什么,并决定更优化的内容。但是正如亚当在他的评论中所说的那样,在这么小的事情中,您最有可能最好关注可读性/可维护性而不是性能。

EDIT: you can view the IL by using the ILDASM.exe that ships with visual studio and open your compiled assembly.

编辑:您可以使用 Visual Studio 附带的 ILDASM.exe 查看 IL,并打开您编译的程序集。

回答by Stephen Hewlett

I just tried this in C# - very quickly, so there could be an error in my method. I used the following code and determined that the second method took about 1.75 times longer than the first.
@Lockszmith: After the edit below, the ratio was 1.115 in favor of the 1st implementation

我刚刚在 C# 中尝试了这个 - 非常快,所以我的方法可能会出错。我使用了以下代码并确定第二种方法比第一种方法长约 1.75 倍。
@Lockszmith:经过下面的编辑,比例为 1.115,支持第一个实现

Even if they took the same time, I would personally use the language construct that is built in, as it expresses your intentions more clearly to any future compiler that may have more built-in optimizations.

即使他们花费相同的时间,我个人也会使用内置的语言结构,因为它可以更清楚地向任何可能具有更多内置优化功能的未来编译器表达您的意图。

@Lockszmith: I've edited the code to reflect the recommendations from the comments

@Lockszmith:我已经编辑了代码以反映评论中的建议

var A = new object();
var B = new object();

var iterations = 1000000000;

var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{   
    if( i == 1 ) sw.Start();
    if (A == null)
    {
        A = B;
    }
}
sw.Stop();
var first = sw.Elapsed;

sw.Reset();
for (int i = 0; i < iterations; i++)
{
    if( i == 1 ) sw.Start();
    A = A ?? B;
}
sw.Stop();
var second = sw.Elapsed;

first.Dump();
second.Dump();

(first.TotalMilliseconds / second.TotalMilliseconds).Dump("Ratio");

回答by l33t

Yes, there is a difference.

是,有一点不同。

Using Visual Studio 2017 15.9.8targeting .NET Framework 4.6.1. Consider the sample below.

使用Visual Studio 2017 15.9.8定位.NET Framework 4.6.1。考虑下面的示例。

static void Main(string[] args)
{
    // Make sure our null value is not optimized away by the compiler!
    var s = args.Length > 100 ? args[100] : null;
    var foo = string.Empty;
    var bar = string.Empty;

    foo = s ?? "foo";
    bar = s != null ? s : "baz";

    // Do not optimize away our stuff above!
    Console.WriteLine($"{foo} {bar}");
}

Using ILDasmit becomes clear that the compiler does not treat those statements equally.

使用ILDasm它很明显编译器不会平等地对待这些语句。

?? operator:

?? 运营商

IL_001c:  dup
IL_001d:  brtrue.s   IL_0025
IL_001f:  pop
IL_0020:  ldstr      "foo"
IL_0025:  ldloc.0

Conditional null check:

条件空检查

IL_0026:  brtrue.s   IL_002f
IL_0028:  ldstr      "baz"
IL_002d:  br.s       IL_0030
IL_002f:  ldloc.0

Apparently, the ??operator implies a duplication of the stack value (should be the svariable right?). I ran a simple test (multiple times) to get a feelingof which of the two is faster. Operating on string, running on this particular machine, I got these average numbers:

显然,该??运算符意味着堆栈值的重复(应该是s变量吧?)。我跑了一个简单的测试(多次)来得到一个感觉这两个的速度更快。string在这台特定机器上运行,我得到了这些平均数字:

?? operator took:           583 ms
null-check condition took: 1045 ms

Benchmark sample code:

基准示例代码:

static void Main(string[] args)
{
    const int loopCount = 1000000000;
    var s = args.Length > 1 ? args[1] : null; // Compiler knows 's' can be null
    int sum = 0;

    var watch = new System.Diagnostics.Stopwatch();
    watch.Start();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s ?? "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"?? operator took {watch.ElapsedMilliseconds} ms");

    sum = 0;

    watch.Restart();

    for (int i = 0; i < loopCount; i++)
    {
        sum += (s != null ? s : "o").Length;
    }

    watch.Stop();

    Console.WriteLine($"null-check condition took {watch.ElapsedMilliseconds} ms");
}

So the answer is yes, there is a difference.

所以答案是肯定的,是有区别的。

PS. StackOverflow should auto-warn posts mentioning "performance" and "negligible" in the same sentence. Only the original poster can know for sure if a time unit is neglible.

附注。StackOverflow 应该自动警告在同一句话中提到“性能”和“可忽略不计”的帖子。只有原始发布者才能确定时间单位是否可以忽略不计。