6

For instance, let's say I call this method:

return bool.TryParse(s, out _);

Is this any more efficient than calling it this way:

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

?

ekolis
  • 6,270
  • 12
  • 50
  • 101
  • 1
    No, they are the same. The compiler creates a dummy variable for you: https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLIN7LpGYZQAs6AsgBRRwAM6AzgJToFLFfoCWAZumo8AdgBcAdABUATgE8ACgENpTAKbUmAGnQB7AK6j0AN2XoAHizYduNzHACc1AEQBNRbKcsA3IVsBfXyIApD8gA= – Dennis_E Sep 04 '19 at 18:33
  • `out var` as far as I know is a syntactic sugar, so I don't think there's any improvement – dcg Sep 04 '19 at 18:33
  • 1
    If the compiler can optimize a discard, it should be able to optimize an unused variable. – Theraot Sep 04 '19 at 18:35
  • 1
    The docs promise too much. Syntax sugar is the primary goal, advantage is that you can use it multiple times inside a method body for different variable types. – Hans Passant Sep 04 '19 at 18:53
  • 2
    Related: https://stackoverflow.com/a/57099575 Eric Lippert *ripped into* the Tour of .NET doc page and even filed a bug against the documentation repo. – madreflection Sep 04 '19 at 19:03
  • 2
    @Theraot: The C# compiler can (and does) remove an unused local variable, but passing a variable by reference as `out` is not "unused" because some storage must exist for the callee to write into, even if that value is never read. – Eric Lippert Sep 04 '19 at 20:25

2 Answers2

11

Let's not trust anything and measure with BenchmarkDotNet.

Here's my code:

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 _);
    }
}

Here's the results:

|             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 |

Looks like there's no difference. Let's see if it compiles to the same 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

So, there is no difference whatsoever.

pneuma
  • 917
  • 5
  • 10
-5

Using the discard operator would be more efficient since an actual variable isn't created along with a storage location in memory for that variable.

The only time you would have a chance to notice any real impact would be if you were doing something in a very large loop.

Supporting MS documentation: https://learn.microsoft.com/en-us/dotnet/csharp/discards

Discards are equivalent to unassigned variables; they do not have a value. Because there is only a single discard variable, and that variable may not even be allocated storage, discards can reduce memory allocations.

Gary Stewart
  • 164
  • 5
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/198977/discussion-on-answer-by-gary-stewart-does-using-a-discard-operator-in-c-opt). – Samuel Liew Sep 04 '19 at 23:54