C#7: Underscore ( _ ) & Star ( * ) in Out Variable

C#7: Underscore ( _ ) & Star ( * ) in Out variable

Discards, in C#7 can be used wherever a variable is declared, to - as the name suggests - discard the result. So a discard can be used with out variables:

p.GetCoordinates(out var x, out _);

and it can be used to discard an expression result:

_ = 42;

In the example,

p.GetCoordinates(out var x, out _);
_ = 42;

There is no variable, _, being introduced. There are just two cases of a discard being used.

If however, an identifier _ exists in the scope, then discards cannot be used:

var _ = 42;
_ = "hello"; // error - a string cannot explicitly convert from string to int

The exception to this is when a _ variable is used as an out variable. In this case, the compiler ignores the type or var and treats it as a discard:

if (p.GetCoordinates(out double x, out double _))
{
_ = "hello"; // works fine.
Console.WriteLine(_); // error: _ doesn't exist in this context.
}

Note that this only occurs if, in this case, out var _ or out double _ is used. Just use out _ and then it's treated as a reference to an existing variable, _, if it's in scope, eg:

string _;
int.TryParse("1", out _); // complains _ is of the wrong type

Finally, the * notation was proposed early in the discussions around discards, but was abandoned in favour of _ due to the latter being a more commonly used notation in other languages.

out var _ and out _ difference?

As opposed to what others in their comments said: No, there are no differences. They both produce the exact same IL.

Both

byte.TryParse(p.Value, out _);
Console.WriteLine(_);

and

byte.TryParse(p.Value, out var _);
Console.WriteLine(_);

will produce a compiler error with C#7, since _ is not intended to be used.

The usage of _ is not restricted to out parameters, but can be used for returns, too (as Evk pointed out)

byte.TryParse(p.Value, out var _); // I don't care about the out variable
_ = SomeMethod(); // I don't care about the return value

There is an excellent answer covering most things about ommitted parameters here.

Remarks: I would prefer out _ over out var _, since there is a clear syntactic distinction between out _ and out var legalVariableName.

EDIT

Obviously I'm not quite right here. There are some subtle differences, see Ben Voigts answer.

C# 7.0 discard out parameter ambiguity

Here's the GitHub issue that details the behavior of discards, the relevant parts are:

However, semantically we want this to create an anonymous variable, and shadow any true variable (e.g. parameter or field) from an enclosing scope named _.

...

We have to be careful with these changes so that any program that uses _ as an identifier and is legal today continues to compile with the same meaning under these revised rules.

_ only means "discard this parameter" if you declare it and use it

  • as an identifier in a "designator" (declaration expression), which includes out parameters and deconstruction of tuples and similar
  • as an indentifier in pattern matching (switch ... case int _)
  • apparently also for declaring anonymous delegates that has to take parameters in order to fit a delegate type but where you don't require the actual parameter.

The out var _ is one of these into this category.

However, int _; is a separate statement, and thus this signals that you "care" about this variable and thus it isn't a discard.

Thus you get a normal variable with the name _, which is still legal. This gets the value 2 from the method call, as expected.

C# 7.0 type pattern matching usage without variable

It's called a discard and yes it is part of the C#7 specification.

From the linked article:

Discards are local variables which you can assign but cannot read from. i.e. they are “write-only” local variables. They don’t have names, instead, they are represented as a _ is a contextual keyword, it is very similar to var, and _ cannot be read (i.e. cannot appear on the right side of an assignment.)

By naming the variable _ you tell the compiler that you will never access this variable again, so it can ignore the problems you have in your first two versions.

How to explicitly discard an out argument?

Starting with C# 7.0, it is possible to avoid predeclaring out parameters as well as ignoring them.

public void PrintCoordinates(Point p)
{
p.GetCoordinates(out int x, out int y);
WriteLine($"({x}, {y})");
}

public void PrintXCoordinate(Point p)
{
p.GetCoordinates(out int x, out _); // I only care about x
WriteLine($"{x}");
}

Source: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/

Deconstruct with one parameter not working

Deconstruction requires at least two variables to deconstruct to.

Otherwise, the expression (id) = new Person() would be ambiguous between normal assignment and deconstructing assignment.

You can also see this from the other compiler error your code gives: Syntax error, ',' expected.

optional/null-able OUT parameter in C#

That looks fine to me. A out cannot be optional for technical reasons (it needs to point to a valid instance).



Related Topics



Leave a reply



Submit