在C#中使用废弃运算符“_”是否能优化代码?

7
例如,假设我调用了这个方法:
return bool.TryParse(s, out _);

这种方式比直接调用更有效率吗?

return bool.TryParse(s, out var dummy);

?


1
不,它们是相同的。编译器为您创建了一个虚拟变量:https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLIN7LpGYZQAs6AsgBRRwAM6AzgJToFLFfoCWAZumo8AdgBcAdABUATgE8ACgENpTAKbUmAGnQB7AK6j0AN2XoAHizYduNzHACc1AEQBNRbKcsA3IVsBfXyIApD8gA= - Dennis_E
据我所知,"out var"是一种语法糖,因此我认为没有任何改进。 - dcg
1
如果编译器能够优化废弃的内容,那么它应该也能够优化未使用的变量。 - Theraot
1
文档承诺过多。语法糖是主要目标,优点是你可以在方法体中多次使用它来处理不同的变量类型。 - Hans Passant
2
相关链接:https://dev59.com/L7Xna4cB1Zd3GeqPGS1Z#57099575 Eric Lippert 猛烈批评了 .NET文档页面的巡回介绍,甚至向文档存储库提交了一个错误报告。 - madreflection
2
@Theraot:C#编译器可以(并且确实)删除未使用的局部变量,但通过引用作为“out”传递变量不是“未使用”,因为一些存储必须存在于被调用者中以便写入,即使该值从未被读取。 - Eric Lippert
2个回答

12

不要轻易相信任何东西,使用进行性能测试。

这是我的代码:

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace Measure
{
    public static class Program
    {
        static void Main(string[] args) => BenchmarkRunner.Run(typeof(Program).Assembly);
    }

    public class Bench
    {
        [Params("true", "false", "invalid")] public string Input { get; set; }

        [Benchmark]
        public bool IsBoolWithVariable() => bool.TryParse(Input, out var result);

        [Benchmark]
        public bool IsBoolDiscarding() => bool.TryParse(Input, out _);
    }
}

以下是结果:

|             Method |   Input |      Mean |     Error |    StdDev |
|------------------- |-------- |----------:|----------:|----------:|
| IsBoolWithVariable |   false |  7.483 ns | 0.0069 ns | 0.0058 ns |
|   IsBoolDiscarding |   false |  7.479 ns | 0.0040 ns | 0.0034 ns |
| IsBoolWithVariable | invalid | 15.802 ns | 0.0051 ns | 0.0043 ns |
|   IsBoolDiscarding | invalid | 15.838 ns | 0.0043 ns | 0.0038 ns |
| IsBoolWithVariable |    true |  7.055 ns | 0.0053 ns | 0.0047 ns |
|   IsBoolDiscarding |    true |  7.104 ns | 0.0407 ns | 0.0381 ns |

看起来没有区别。让我们看看它是否编译为相同的 IL 代码:

IsBoolDiscarding()

    IL_0000: ldarg.0      // this
    IL_0001: call         instance string Measure.Bench::get_Input()
    IL_0006: ldloca.s     V_0
    IL_0008: call         bool [System.Runtime]System.Boolean::TryParse(string, bool&)
    IL_000d: ret

IsBoolWithVariable()

    IL_0000: ldarg.0      // this
    IL_0001: call         instance string Measure.Bench::get_Input()
    IL_0006: ldloca.s     result
    IL_0008: call         bool [System.Runtime]System.Boolean::TryParse(string, bool&)
    IL_000d: ret

所以,实际上没有任何区别。


3
先看IL可能会更容易。这样可以避免一些基准测试的麻烦 ;) - adjan
2
是的,但如果不这样做,我就永远不会知道Benchmark.NET有多棒了! ;) - ekolis

-6

使用丢弃运算符会更有效率,因为实际变量不会被创建,也不需要在内存中为该变量分配存储位置。

唯一可能会注意到任何真正影响的时间是如果您正在执行非常大的循环操作。

支持微软文档:https://learn.microsoft.com/en-us/dotnet/csharp/discards

丢弃与未分配变量等效;它们没有值。由于只有一个丢弃变量,并且该变量甚至可能没有分配存储空间,因此丢弃可以减少内存分配。


评论不适合进行长时间的讨论;此对话已被移至聊天室 - Samuel Liew

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