【问题标题】:C#7: Underscore ( _ ) & Star ( * ) in Out variableC#7:Out 变量中的下划线 (_) 和星号 (*)
【发布时间】:2017-08-12 17:39:40
【问题描述】:

我正在阅读 C#7 here 中的新变量特性。我有两个问题:

  1. 它说

    我们也允许“丢弃”作为输出参数,以_ 的形式,让您忽略您不关心的参数:

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

    问:我想这只是一个信息,而不是 C#7 的新功能,因为我们也可以在 C#7.0 之前这样做:

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

    还是我在这里遗漏了什么?

  2. 当我按照同一篇博客中的说明进行操作时,我的代码会出错:

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

    * 不是有效标识符。我猜是 Mads Torgersen 的疏忽?

【问题讨论】:

  • in out _ _ 不是变量,您不声明它,也不能按名称使用它。在int _ 中,这是一个变量。
  • 星号通配符似乎没有进入 C# 7 的最终版本。
  • @NikhilAgrawal 但这是不同的语法。在您的问题中,您使用out _,而不使用varvar 确实和以前一样。
  • @NikhilAgrawal 确实出于某种奇怪的原因编译。但是,如果查看反编译的代码——这个赋值是不存在的,完全被删除了。而且,如果您执行Console.WriteLine(_) 之类的操作,则不会编译声称没有这样的变量。很奇怪。更重要的是:如果您执行 _ = SomeMethodCall() 之类的操作 - 在编译后的代码中,这将被替换为 SomeMethodCall()。所以毕竟你仍然不能在任何有意义的意义上真正使用这个变量。

标签: c# c#-7.0


【解决方案1】:

问:......我们也可以在 C#7.0 之前这样做:

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

还是我在这里遗漏了什么?

这不是一回事。
您的代码正在分配。

在 C# 7.0 _ 不是变量,它告诉编译器丢弃该值
除非您已将 _ 声明为变量...如果您这样做,则使用该变量而不是丢弃符​​号)

示例:您可以在同一行代码中将 _ 用作 sting 和 int:

string a; 
int b;
Test(out a, out b);
Test(out _, out _);

//...

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

【讨论】:

    【解决方案2】:

    在 C# 7.0(2017 年 3 月左右的 Visual Studio 2017)中,以下上下文中的分配支持丢弃:


    其他有用的说明

    • 丢弃可以减少内存分配。因为他们做出了意图 您的代码清晰,它们增强了它的可读性和可维护性
    • 请注意,_ 也是一个有效的标识符。在外面使用时 支持的上下文

    简单的例子:这里我们不想使用第一个和第二个参数,只需要第三个参数

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

    开关案例中的高级示例也使用了现代开关案例模式匹配 (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;
    

    }

    【讨论】:

      【解决方案3】:

      Discards,在 C#7 中,可以在任何声明变量的地方使用,以 - 顾名思义 - 丢弃结果。所以可以在没有变量的情况下使用丢弃:

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

      它可以用来丢弃一个表达式结果:

      _ = 42;
      

      在示例中,

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

      没有引入变量_。只有两种使用丢弃的情况。

      但是,如果作用域中存在标识符_,则不能使用丢弃:

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

      这种情况的例外是_ 变量用作输出变量。在这种情况下,编译器会忽略类型或var,并将其视为丢弃:

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

      请注意,只有在这种情况下使用out var _out double _ 时才会发生这种情况。只需使用out _,然后将其视为对现有变量_ 的引用,如果它在范围内,例如:

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

      最后,* 符号是在围绕丢弃的讨论的早期提出的,but was abandoned in favour of _ due to the latter being a more commonly used notation in other languages

      【讨论】:

      • 我认为这是隐含的,但丢弃的重点是它的预期值从未实际存储?
      • 声明_ = 42 "discard[s] the expression result" 具有误导性,因为_ = 42 本身就是一个值为42 的表达式,因此实际上并没有进行丢弃。仍然存在差异,因为 _ = 42; 也是一个声明,而 42; 不是,这在某些情况下很重要。
      • @JeroenMostert,你会如何措辞不同?表达式被丢弃,因为使用了丢弃。虽然_ = 42' 是一个表达式语句是对的,但我不清楚这与正在使用丢弃的事实有什么关系,因此表达式的结果不会存储在任何地方;它被丢弃了。
      • 措辞很好,只是_ = 42 未能说明这个丢弃的意义——即,当你需要“不存储”一个表达式但无论如何都要评估它时,看到就像您通常如何在不存储它的情况下评估(非平凡的,有用的)表达式一样。我自己无法立即想到一个有用的例子(我不知道是否有一个,或者这是否只是语法一致性的结果)。
      • @JeroenMostert,嗯,好的。得到你。是的,一个比42 更好的例子会很有用。我会考虑一个更好的例子并更新答案。谢谢。
      【解决方案4】:

      C# 7 中丢弃运算符_ 的另一个示例是在最近添加到C# 7 中的switch 语句中pattern match 类型为object 的变量:

      代码:

      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
      }
      

      此代码将匹配类型并丢弃传递给case ... _的变量。

      【讨论】:

        【解决方案5】:

        更多好奇

        考虑以下sn-p

        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)
        {
            //...
        }
        

        这是正在发生的事情:

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

        正如您在幕后看到的那样,这两个电话正在做同样的事情。

        正如@Servé Laurijssen 所指出的,很酷的事情是您不必预先声明 变量,如果您对某些值不感兴趣,这很方便。

        【讨论】:

        • IL 必须 相同,因为您调用的函数仍然需要用于输出变量的插槽。只是使用新的丢弃语法允许编译器对局部变量(或者更确切地说是缺少)做出进一步的假设,允许它更有效地使用它(至少在理论上;我不知道是否已经有任何优化截至目前在编译器中)。
        【解决方案6】:

        关于第一个问题

        我猜这只是一个信息,而不是 C#7 的新特性,因为我们可以 在 C#7.0 之前的版本中也可以这样做。

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

        新颖之处在于您不必再在表达式内部或外部声明_,您只需键入

        int.TryParse(s, out _);
        

        尝试在 C#7 之前执行此操作:

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

        【讨论】:

        • 补充:下划线非常适用于具有多个输出参数的方法,例如,SomeMethod(out _, out _, out three) 有 3 个输出参数,但我放弃了前两个,而不必创建像 @987654326 这样的变量@等
        • @MichaelStum:这里发生了什么? if (SomeMethod(out _, out _, out _)) _ = 5; 指的是哪个_
        • @NikhilAgrawal 根本不会有_ 变量,即使您使用out var _。似乎下划线是特殊情况以丢弃结果。
        猜你喜欢
        • 2011-03-09
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 2011-04-08
        • 2020-08-24
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        相关资源
        最近更新 更多