• C#各版本新功能 C#7.0


    out 变量

    (以前必须在外面定义一行才可以使用)

    if (int.TryParse(input, out int result))
        Console.WriteLine(result);

    元组

    元组(Tuple)在 .Net 4.0 的时候就有了,但元组也有些缺点,如:
    1)Tuple 会影响代码的可读性,因为它的属性名都是:Item1,Item2.. 。
    2)Tuple 还不够轻量级,因为它是引用类型(Class)。
    备注:上述所指 Tuple 还不够轻量级,是从某种意义上来说的或者是一种假设,即假设分配操作非常的多。
    C# 7 中的元组(ValueTuple)解决了上述两个缺点:
    1)ValueTuple 支持语义上的字段命名。
    2)ValueTuple 是值类型(Struct)。

    • 如何创建一个元组?
    var tuple = (1, 2); //使用语法糖创建元组
    var tuple2 = ValueTuple.Create(1, 2); // 使用静态方法【Create】创建元组
    var tuple3 = new ValueTuple<int, int>(1, 2);  // 使用 new 运算符创建元组
    WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三种方式都是等价的。");
    • 如何创建给字段命名的元组?
      以前的元组元素只能是item1 itme2 ,现在可以起有意义的名字了
    // 左边指定字段名称
    (string Alpha, string Beta) namedLetters = ("a", "b");
    Console.WriteLine($"{namedLetters.Alpha}, {namedLetters.Beta}");
    //右边指定字段名称
    var alphabetStart = (Alpha: "a", Beta: "b");
    Console.WriteLine($"{alphabetStart.Alpha}, {alphabetStart.Beta}");
    // 左右两边同时指定字段名称
     (int one, int two) tuple3 = (first: 1, second: 2);    /* 此处会有警告:由于目标类型(xx)已指定了其它名称,因为忽略元组名称xxx */
    WriteLine($"first:{tuple3.one}, second:{tuple3.two}");
    //注:左右两边同时指定字段名称,会使用左边的字段名称覆盖右边的字段名称(一一对应)。
    //原理解析:上述给字段命名的元组在编译后其字段名称还是:Item1, Item2...,即:“命名”只是语义上的命名

    析构元组 (Deconstructing tuples)

    public class Example
    {
        public static void Main()
        {
            var result = QueryCityData("New York City"); //之前 我需要定义三个变量去接收这个值
            var city = result.Item1;
            var pop = result.Item2;
            var size = result.Item3
             // Do something with the data.
        }
        private static (string, int, double) QueryCityData(string name)
        {
            if (name == "New York City")
                return (name, 8175133, 468.48);
            return ("", 0, 0);
        }
    }
    // 现在单个操作中解包一个元组中的所有项
    (int max, int min) = Range(numbers);// 析构元组,定义一个变量就可以了,比上面定义三行要简单明了
    Console.WriteLine(max);

    有三种方法可用于析构元组:

    1. 可以在括号内显式声明每个字段的类型
      ( string city, int population, double area) = QueryCityData( "New York City" );
    2. 可使用 var 关键字,以便 C# 推断每个变量的类型
      var (city, population, area) = QueryCityData( "New York City" );// 将 var 关键字放在括号外

    ( string city, var population, var area) = QueryCityData( "New York City" ); // 将 var 关键字单独与任一或全部变量声明结合使用 不建议

    1. 可将元组析构到已声明的变量中
      [注意事项] (https://docs.microsoft.com/zh-cn/dotnet/csharp/deconstruct)

    使用弃元析构元组

    var (_, _, _, pop1, _, pop2) = QueryCityDataForYears( "New York City" , 1960 , 2010 );
    Console.WriteLine( $"Population change, 1960 to 2010: {pop2 - pop1:N0} " );
    

    析构用户自定义类型

    作为类,解构或者接口的创建者,可以通过一个或者多个 Deconstruct 方法来析构该类型的实例,该方法返回void,并且析构的每一个值都需要用out参数标志;

    public class Point
       {
           public Point(double x, double y)
               => (X, Y) = (x, y);
           public double X { get; }
           public double Y { get; }
           public void Deconstruct(out double x, out double y) =>
               (x, y) = (X, Y);
       }
       //You can extract the individual fields by assigning a Point to a tuple:
       var p = new Point(3.14, 2.71);
    (double X, double Y) = p;

    析构对象时的重载方法限制

    使用弃元析构用户类型

    var (fName, _, city, _) = p;
    Console.WriteLine( $"Hello {fName} of {city} !" );
    

    使用扩展方法析构用户自定义类型

    详细链接

    弃元

    使用弃元析构元组和自定义类型

        public static void Main()
        {//我只需要第四个值跟第6个值 其它的我不关心;
            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

    当使用外参的时候

        string l_strDate = "2019-01-01";
        if (DateTime.TryParse(l_strDate,out _)) //以前需要写..
        {
            Console.WriteLine(DateTime.Parse(l_strDate));
        }

    在模式匹配操作 is switch语句中

    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;
          }
       }

    数字,二进制的分隔符

    增强文本可读性

    // 二进制文本:
    public const int Sixteen = 0b0001_0000;
    // 数字分隔符:
    public const long BillionsAndBillions = 100_000_000_000;

    独立弃元

            private static void ShowValue(int _)
            {
                byte[] arr = { 0, 0, 1, 2 };
                _ = BitConverter.ToInt32(arr, 0);
                Console.WriteLine(_);
            }

    模式匹配

    现在可以在匹配一个类型时,自动转换为这个类型的变量,如果转换失败,这个变量就赋值为默认值(null或0)

    if (input is int count) //
        sum += count;
    public static int SumPositiveNumbers(IEnumerable<object> sequence)
    {
        int sum = 0;
        foreach (var i in sequence)
        {
            switch (i)
            {
                case 0:
                    break;
                case IEnumerable<int> childSequence: //模式匹配  匹配是 IEnumerable<int> 类型的
                {
                    foreach(var item in childSequence)
                        sum += (item > 0) ? item : 0;
                    break;
                }
                case int n when n > 0: //模式匹配  匹配是int 类型的
                    sum += n;
                    break;
                case null: //is the null pattern
                    throw new NullReferenceException("Null found in sequence");
                default:
                    throw new InvalidOperationException("Unrecognized type");
            }
        }
        return sum;
    }

    局部变量和返回结果

    我们知道 C# 的 ref 和 out 关键字是对值传递的一个补充,是为了防止值类型大对象在Copy过程中损失更多的性能。现在在C# 7中 ref 关键字得
    到了加强,它不仅可以获取值类型的引用而且还可以获取某个变量(引用类型)的局部引用。如:

    static ref int GetLocalRef(int[,] arr, Func<int, bool> func)
      {
          for (int i = 0; i < arr.GetLength(0); i++)
          {
              for (int j = 0; j < arr.GetLength(1); j++)
              {
                  if (func(arr[i, j]))
                  {
                      return ref arr[i, j];
                  }
              }
          }
          throw new InvalidOperationException("Not found");
      }
    
       int[,] arr = { { 10, 15 }, { 20, 25 } };
       ref var num = ref GetLocalRef(arr, c => c == 20);
       num = 600;
       Console.WriteLine(arr[1, 0]); //output 600

    使用方法:
    1.方法的返回值必须是引用返回

    • 声明方法签名时必须在返回类型前加上 ref 修饰
    • 每个 return 关键字后也要加上 ref 修饰,以表明是返回引用

    2.分配引用(即赋值),必须在声明局部变量前加上 ref 修饰,以及在方法返回引用前加上 ref 修饰。
    注:C# 开发的是托管代码,所以一般不希望程序员去操作指针。并由上述可知在使用过程中需要大量的使用 ref 来标明这是引用变量(编译后其实没那么多),当然这也是为了提高代码的可读性。
    总结:虽然 C# 7 中提供了局部引用和引用返回,但为了防止滥用所以也有诸多约束

    1. 你不能将一个值分配给 ref 变量
    ref int num = 10;   // error:无法使用值初始化按引用变量
    1. 你不能返回一个生存期不超过方法作用域的变量引用,如:
    public ref int GetLocalRef(int num) => ref num;   // error: 无法按引用返回参数,因为它不是 ref 或 out 参数
    1. ref 不能修饰 “属性” 和 “索引器”。
    var list = new List<int>();
     ref var n = ref list.Count;  // error: 属性或索引器不能作为 out 或 ref 参数传递

    局部函数(Local functions)

    一个函数在另外一个函数的里面

    public static IEnumerable<char> AlphabetSubset3(char start, char end)
    {
        if (start < 'a' || start > 'z')
            throw new ArgumentOutOfRangeException(paramName: nameof(start), message: "start must be a letter");
        if (end < 'a' || end > 'z')
            throw new ArgumentOutOfRangeException(paramName: nameof(end), message: "end must be a letter");
        if (end <= start)
            throw new ArgumentException($"{nameof(end)} must be greater than {nameof(start)}");
        return alphabetSubsetImplementation();
        IEnumerable<char> alphabetSubsetImplementation()
        {
            for (var c = start; c < end; c++)
                yield return c;
        }
    }

    更多的表达式方法体

    C# 6 的时候就支持表达式体成员,但当时只支持“函数成员”和“只读属性”,这一特性在C# 7中得到了扩展,它能支持更多的成员:构造函数
    、析构函数、带 get,set 访问器的属性、以及索引器。如下所示

    // Expression-bodied constructor
    public ExpressionMembersExample(string label) => this.Label = label;
    // Expression-bodied finalizer
    ~ExpressionMembersExample() => Console.Error.WriteLine("Finalized!");
    
    private string label;
    
    // Expression-bodied get / set accessors.
    public string Label
    {
        get => label;
        set => this.label = value ?? "Default label";
    }
    
    // Expression-bodied indexers
    public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));

    Throw expressions

    throw之前必须是一个语句,因此有时不得不写更多的代码来完成所需功能。但7.0提供了throw表达式来使代码更简洁,阅读更轻松。

    void Main()
    {
        // You can now throw expressions in expressions clauses.
        // This is useful in conditional expressions:
        string result = new Random().Next(2) == 0 ? "Good" : throw new Exception ("Bad");
        result.Dump();
    
        Foo().Dump();
    }
    
    public string Foo() => throw new NotImplementedException();

    扩展异步返回类型(Generalized async return types)

    以前异步的返回类型必须是:Task、Task、void,现在 C# 7 中新增了一种类型:ValueTask,如下所示:

    public async ValueTask<int> Func()
    {
        await Task.Delay(100);
        return 5;
    }

    这样可以节省空间,尤其是在循环里面

  • 相关阅读:
    学习网页栅格系统的几篇好文
    [转载]iis6配置使用页面Gzip压缩提速
    img标签的src=""会引起的Page_Load多次执行
    基于sliverlight + wcf的web 文字版IM 示例
    Enterprise Library 4.1学习笔记8缓存应用程序块之FileDependency
    windows 2008上启用防火墙后sqlserver 2005经常出现连接超时的解决办法
    负载均衡环境下的web服务器处理
    Ado.Net连接池的速度测试
    [转载]网页栅格系统研究(1):960的秘密
    css基础:把所有背景图都集成在一张图片上,减少图片服务器请求次数
  • 原文地址:https://www.cnblogs.com/maanshancss/p/8854f239ffb9d072e1373cd1205bf6ac.html
Copyright © 2020-2023  润新知