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

后端 未结 6 604
情书的邮戳
情书的邮戳 2020-11-27 17:08

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 w

相关标签:
6条回答
  • 2020-11-27 17:17

    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.

    0 讨论(0)
  • 2020-11-27 17:19

    In C# 7.0 (Visual Studio 2017 around March 2017), discards are supported in assignments in the following contexts:

    • Tuple and object deconstruction.
    • Pattern matching with is and switch.
    • Calls to methods with out parameters.
    • A standalone _ when no _ is in scope.

    Other useful notes

    • discards can reduce memory allocations. Because they make the intent of your code clear, they enhance its readability and maintainability
    • Note that _ is also a valid identifier. When used outside of a supported context

    Simple example : here we do not want to use the 1st and 2nd params and only need the 3rd param

    (_, _, area) = city.GetCityInformation(cityName);
    

    Advanced example in switch case which used also modern switch case pattern matching (source)

    switch (exception)                {
    case ExceptionCustom exceptionCustom:       
            //do something unique
            //...
        break;
    case OperationCanceledException _:
        //do something else here and we can also cast it 
        //...
        break;
    default:
        logger?.Error(exception.Message, exception);
        //..
        break;
    

    }

    0 讨论(0)
  • 2020-11-27 17:21

    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 _);
    }
    
    0 讨论(0)
  • 2020-11-27 17:26

    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.

    0 讨论(0)
  • 2020-11-27 17:30

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

    0 讨论(0)
  • 2020-11-27 17:32

    Q: ... we can do so in pre C#7.0 too:

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

    or am I missing something here?

    That isn't the same thing.
    Your code is making an assignment.

    In C# 7.0 _ is not a variable, it tells the compiler to discard the value
    (unless you have declared _ as a variable... if you do that the variable is used instead of the discard symbol)

    Example: you can use _ as a sting and an int in the same line of code:

    string a; 
    int b;
    Test(out a, out b);
    Test(out _, out _);
    
    //...
    
    void Test(out string a, out int b)
    {
       //...
    }
    
    0 讨论(0)
提交回复
热议问题