• 弃元


    从 C# 7.0 开始,C# 支持弃元,这是一种在应用程序代码中人为取消使用的占位符变量。 弃元相当于未赋值的变量;它们没有值。 因为只有一个弃元变量,甚至不为该变量分配存储空间,所以弃元可减少内存分配。 因为它们使代码的意图清楚,增强了其可读性和可维护性。

    通过将下划线 (_) 赋给一个变量作为其变量名,指示该变量为一个占位符变量。 例如,下面的方法调用返回 3 元组,其中的第一个和第二个值是弃元,area 是一个以前声明的变量,它将设置为 GetCityInformation 返回的相应的第三个部分:

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

    在 C# 7.0 及更高版本中,支持在以下上下文的分配中使用弃元:

    • 元组和对象析构
    • 使用 is 和 switch 的模式匹配。
    • 对具有 out 参数的方法的调用。
    • 当范围内没有 _ 时,独立的 _

    从 C# 9.0 开始,可以使用弃元指定 Lambda 表达式中不使用的输入参数。 有关详细信息,请参阅 Lambda 表达式一文中的 Lambda 表达式的输入参数一节。

    当 _ 是有效占位符时,尝试检索其值或在赋值操作中使用它时会生成编译器错误 CS0301:当前上下文中不存在名称 "_"。 这是因为 _ 未赋值,甚至可能未分配存储位置。 如果它是一个实际变量,则不能像之前的示例那样对多个值使用占位符。

    元组和对象析构

    如果应用程序代码使用某些元组元素,但忽略其他元素,这时使用占位符来处理元组就会特别有用。 例如,以下的 QueryCityDataForYears 方法返回一个 6 元组,包含城市名称、城市面积、一个年份、该年份的城市人口、另一个年份及该年份的城市人口。 该示例显示了两个年份之间人口的变化。 对于元组提供的数据,我们不关注城市面积,并在一开始就知道城市名称和两个日期。 因此,我们只关注存储在元组中的两个人口数量值,可将其余值作为占位符处理。

     
    using System;
    using System.Collections.Generic;
    
    public class Example
    {
        public static void Main()
        {
            var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
    
            Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
        }
    
        private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
        {
            int population1 = 0, population2 = 0;
            double area = 0;
    
            if (name == "New York City")
            {
                area = 468.48;
                if (year1 == 1960)
                {
                    population1 = 7781984;
                }
                if (year2 == 2010)
                {
                    population2 = 8175133;
                }
                return (name, area, year1, population1, year2, population2);
            }
    
            return ("", 0, 0, 0, 0, 0);
        }
    }
    // The example displays the following output:
    //      Population change, 1960 to 2010: 393,149

    有关使用占位符析构元组的详细信息,请参阅 析构元组和其他类型

    类、结构或接口的 Deconstruct 方法还允许从对象中检索和析构一组特定的数据。 如果想只使用析构值的一个子集时,可使用弃元。 以下示例将 Person 对象析构为四个字符串(名字、姓氏、城市和省/市/自治区),但舍弃姓氏和省/市/自治区。

    using System;
    
    public class Person
    {
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }
        public string State { get; set; }
    
        public Person(string fname, string mname, string lname,
                      string cityName, string stateName)
        {
            FirstName = fname;
            MiddleName = mname;
            LastName = lname;
            City = cityName;
            State = stateName;
        }
    
        // Return the first and last name.
        public void Deconstruct(out string fname, out string lname)
        {
            fname = FirstName;
            lname = LastName;
        }
    
        public void Deconstruct(out string fname, out string mname, out string lname)
        {
            fname = FirstName;
            mname = MiddleName;
            lname = LastName;
        }
    
        public void Deconstruct(out string fname, out string lname,
                                out string city, out string state)
        {
            fname = FirstName;
            lname = LastName;
            city = City;
            state = State;
        }
    }
    
    public class Example
    {
        public static void Main()
        {
            var p = new Person("John", "Quincy", "Adams", "Boston", "MA");
    
            // <Snippet1>
            // Deconstruct the person object.
            var (fName, _, city, _) = p;
            Console.WriteLine($"Hello {fName} of {city}!");
            // The example displays the following output:
            //      Hello John of Boston!
            // </Snippet1>
        }
    }
    // The example displays the following output:
    //    Hello John Adams of Boston, MA!

    有关使用弃元析构用户定义的类型的详细信息,请参阅 析构元组和其他类型

    使用 switch 和 is 的模式匹配

    弃元模式可通过 is 和 switch 关键字用于模式匹配。 每个表达式始终匹配弃元模式。

    以下示例定义了一个 ProvidesFormatInfo 方法,该方法使用 is 语句来确定对象是否提供 IFormatProvider 实现并测试对象是否为 null。 它还使用占位符模式来处理任何其他类型的非 null 对象。

    using System;
    using System.Globalization;
    
    public class Example
    {
       public static void Main()
       {
          object[] objects = { CultureInfo.CurrentCulture,
                               CultureInfo.CurrentCulture.DateTimeFormat,
                               CultureInfo.CurrentCulture.NumberFormat,
                               new ArgumentException(), null };
          foreach (var obj in objects)
             ProvidesFormatInfo(obj);
       }
    
       private static void ProvidesFormatInfo(object obj)
       {
          switch (obj)
          {
             case IFormatProvider fmt:
                Console.WriteLine($"{fmt} object");
                break;
             case null:
                Console.Write("A null object reference: ");
                Console.WriteLine("Its use could result in a NullReferenceException");
                break;
             case object _:
                Console.WriteLine("Some object type without format information");
                break;
          }
       }
    }
    // The example displays the following output:
    //    en-US object
    //    System.Globalization.DateTimeFormatInfo object
    //    System.Globalization.NumberFormatInfo object
    //    Some object type without format information
    //    A null object reference: Its use could result in a NullReferenceException

    使用 out 参数调用方法

    当调用 Deconstruct 方法来析构用户定义类型(类、结构或接口的实例)时,可使用占位符表示单个 out 参数的值。 但当使用 out 参数调用任何方法时,也可使用占位符表示 out 参数的值。

    以下示例调用 DateTime.TryParse(String, out DateTime) 方法来确定日期的字符串表示形式在当前区域性中是否有效。 因为该示例侧重验证日期字符串,而不是解析它来提取日期,所以方法的 out 参数为占位符。

    using System;
    
    public class Example
    {
       public static void Main()
       {
          string[] dateStrings = {"05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
                                  "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
                                  "5/01/2018 14:57:32.80 -07:00",
                                  "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",
                                  "Fri, 15 May 2018 20:10:57 GMT" };
          foreach (string dateString in dateStrings)
          {
             if (DateTime.TryParse(dateString, out _))
                Console.WriteLine($"'{dateString}': valid");
             else
                Console.WriteLine($"'{dateString}': invalid");
          }
       }
    }
    // The example displays output like the following:
    //       '05/01/2018 14:57:32.8': valid
    //       '2018-05-01 14:57:32.8': valid
    //       '2018-05-01T14:57:32.8375298-04:00': valid
    //       '5/01/2018': valid
    //       '5/01/2018 14:57:32.80 -07:00': valid
    //       '1 May 2018 2:57:32.8 PM': valid
    //       '16-05-2018 1:00:32 PM': invalid
    //       'Fri, 15 May 2018 20:10:57 GMT': invalid

    独立弃元

    可使用独立弃元来指示要忽略的任何变量。 以下示例使用独立占位符来忽略异步操作返回的 Task 对象。 这一操作的效果等同于抑制操作即将完成时所引发的异常。

    using System;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static async Task Main(string[] args)
       {
          await ExecuteAsyncMethods();
       }
    
       private static async Task ExecuteAsyncMethods()
       {
          Console.WriteLine("About to launch a task...");
          _ = Task.Run(() => { var iterations = 0;
                               for (int ctr = 0; ctr < int.MaxValue; ctr++)
                                  iterations++;
                               Console.WriteLine("Completed looping operation...");
                               throw new InvalidOperationException();
                             });
          await Task.Delay(5000);
          Console.WriteLine("Exiting after 5 second delay");
       }
    }
    // The example displays output like the following:
    //       About to launch a task...
    //       Completed looping operation...
    //       Exiting after 5 second delay

    请注意,_ 也是有效标识符。 当在支持的上下文之外使用时,_ 不视为占位符,而视为有效变量。 如果名为 _ 的标识符已在范围内,则使用 _ 作为独立占位符可能导致:

    • 将预期的占位符的值赋给范围内 _ 变量,会导致该变量的值被意外修改。 例如:

      private static void ShowValue(int _)
      {
         byte[] arr = { 0, 0, 1, 2 };
         _ = BitConverter.ToInt32(arr, 0);
         Console.WriteLine(_);
      }
      // The example displays the following output:
      //       33619968
    • 因违反类型安全而发生的编译器错误。 例如:

      private static bool RoundTrips(int _)
      {
         string value = _.ToString();
         int newValue = 0;
         _ = Int32.TryParse(value, out newValue);
         return _ == newValue;
      }
      // The example displays the following compiler error:
      //      error CS0029: Cannot implicitly convert type 'bool' to 'int'
    • 编译器错误 CS0136:“无法在此范围中声明名为“_”的局部变量或参数,因为该名称用于在封闭的局部范围中定义局部变量或参数”。 例如:

      public void DoSomething(int _)
      {
       var _ = GetValue(); // Error: cannot declare local _ when one is already in scope
      }
      // The example displays the following compiler error:
      // error CS0136:
      //       A local or parameter named '_' cannot be declared in this scope
      //       because that name is used in an enclosing local scope
      //       to define a local or parameter
      

        

  • 相关阅读:
    自定义UINavigationBar
    UIImage 拉伸图片 resizableImageWithCapInsets
    自定义UINavigationBar上面的按钮
    tableView中deselectRowAtIndexPath的作用
    [LeetCode] Search a 2D Matrix, Solution
    [LeetCode] Longest Valid Parentheses, Solution
    我也写了一个牛顿迭代法,貌似不需要特殊处理溢出的情况 class Solution { public...
    [LeetCode] Remove Duplicates from Sorted List II, Solution
    [LeetCode] Palindrome Partitioning, Solution
    [LeetCode] Two Sum, Solution
  • 原文地址:https://www.cnblogs.com/YourDirection/p/14177178.html
Copyright © 2020-2023  润新知