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

2019-01-07 17:15发布

问题:

I was reading about new out variable features in C#7 here. I have two questions:

  1. It says

    We allow "discards" as out parameters as well, in the form of a _, to let you ignore out parameters you don’t care about:

    p.GetCoordinates(out var x, out _); // I only care about x
    

    Q: I guess this is just an info and not a new feature of C#7 because we can do so in pre C#7.0 too:

    var _;
    if (Int.TryParse(str, out _))
    ...
    

    or am I missing something here?

  2. My code gives an error when I do as mentioned in same blog:

    ~Person() => names.TryRemove(id, out *);
    

    * is not a valid identifier. An oversight by Mads Torgersen I guess?

回答1:

Discards, in C#7 can be used wherever a variable is declared, to - as the name suggests - discard the result. So it 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.



回答2:

Another example of the Discard Operator _ in C# 7 is to pattern match a variable of type object in a switch statement, which was recently added in C# 7:

Code:

static void Main(string[] args)
{
    object x = 6.4; 
    switch (x)
    {
        case string _:
            Console.WriteLine("it is string");
            break;
        case double _:
            Console.WriteLine("it is double");
            break;
        case int _:
            Console.WriteLine("it is int");
            break;
        default:
            Console.WriteLine("it is Unknown type");
            break;
    }

    // end of main method
}

This code will match the type and discard the variable passed to the case ... _.



回答3:

For more curious

Consider the following snippet

static void Main(string[] args)
{
    //....
    int a;
    int b;

    Test(out a, out b);
    Test(out _, out _);    
    //....
}

private static void Test(out int a, out int b)
{
    //...
}

This is what's happening:

...

13:             int  a;
14:             int  b;
15: 
16:             Test(out a, out b);
02340473  lea         ecx,[ebp-40h]  
02340476  lea         edx,[ebp-44h]  
02340479  call        02340040  
0234047E  nop  
    17:             Test(out _, out _);
0234047F  lea         ecx,[ebp-48h]  
02340482  lea         edx,[ebp-4Ch]  
02340485  call        02340040  
0234048A  nop 

...

As you can see behind the scene the two calls are making the same thing.

As @Servé Laurijssen pointed out the cool thing is that you don't have to pre-declare variables which is handy if you are not interested in some values.



回答4:

Regarding the first question

I guess this is just an info and not a new feature of C#7 because we can do so in pre C#7.0 too.

var _;
if (Int.TryParse(str, out _))
    // ...

The novelty is that you dont have to declare _ anymore inside or outside the expression and you can just type

int.TryParse(s, out _);

Try to do this one liner pre C#7:

private void btnDialogOk_Click_1(object sender, RoutedEventArgs e)
{
     DialogResult = int.TryParse(Answer, out _);
}


标签: c# c#-7.0