zoukankan      html  css  js  c++  java
  • C#7.0新特性(VS2017可用)

    分享一下其实2016年12月就已经公布了的C#7.0的新特性吧,虽然很早就出来了,但咱这IDE不支持啊..

    不过在昨天的VS2017中已经完美可以支持使用了.

    E文好的,移步官方介绍地址:https://docs.microsoft.com/zh-cn/dotnet/articles/csharp/csharp-7

    先列一下相关的语法:

    1.out-variables(Out变量)

    2.Tuples(元组)

    3.Pattern Matching(匹配模式)

    4.ref locals and returns (局部变量和引用返回)

    5.Local Functions (局部函数)

    6.More expression-bodied members(更多的函数成员的表达式体)

    7.throw Expressions (异常表达式)

    8.Generalized async return types (通用异步返回类型)

    9.Numeric literal syntax improvements(数值文字语法改进)

    正文

    1. out-variables(Out变量)

    以前,我们使用out变量的时候,需要在外部先申明,然后才能传入方法,类似如下:

    string ddd = ""; //先申明变量
    ccc.StringOut(out ddd);
    Console.WriteLine(ddd);

    在C#7.0中我们可以不必申明,直接在参数传递的同时申明它,如下:

     StringOut(out string ddd); //传递的同时申明
    Console.WriteLine(ddd);
    Console.ReadLine();

    2.Tuples(元组)

    曾今在.NET4.0中,微软对多个返回值给了我们一个解决方案叫元组,类似代码如下:

    复制代码
     static void Main(string[] args)
     {
                var data = GetFullName();
                Console.WriteLine(data.Item1);
                Console.WriteLine(data.Item2);
                Console.WriteLine(data.Item3);
                Console.ReadLine();
    }
    static Tuple<string, string, string> GetFullName() 
    {
               return  new Tuple<string, string, string>("a", "b", "c");
    }
    复制代码

    上面代码展示了一个方法,返回含有3个字符串的元组,然而当我们获取到值,使用的时候 心已经炸了,Item1,Item2,Item3是什么鬼,虽然达到了我们的要求,但是实在不优雅

    那么,在C#7.0中,微软提供了更优雅的方案:(注意:需要通过nuget引用System.ValueTuple)如下:

    复制代码
            static void Main(string[] args)
            {
                var data=GetFullName();
                Console.WriteLine(data.a); //可用命名获取到值
                Console.WriteLine(data.b);
                Console.WriteLine(data.c);
                Console.ReadLine();
    
            }
    
    
            //方法定义为多个返回值,并命名
            private static (string a,string b,string c) GetFullName()
            {
                return ("a","b","c");
            }
    复制代码

    解构元组,有的时候我们不想用var匿名来获取,那么如何获取abc呢?我们可以如下:

    复制代码
     static void Main(string[] args)
            {
               //定义解构元组
                (string a, string b, string c) = GetFullName();
    
                Console.WriteLine(a);
                Console.WriteLine(b);
                Console.WriteLine(c);
                Console.ReadLine();
    
            }
    
    
    
            private static (string a,string b,string c) GetFullName()
            {
                return ("a","b","c");
            }
    复制代码

    3. Pattern Matching(匹配模式)

    在C#7.0中,引入了匹配模式的玩法,先举个老栗子.一个object类型,我们想判断他是否为int如果是int我们就加10,然后输出,需要如下:

    复制代码
    object a = 1;
    if (a is int) //is判断
    {
      int b = (int)a; //拆
      int d = b+10; //加10
      Console.WriteLine(d); //输出
    }
    复制代码

    那么在C#7.0中,首先就是对is的一个小扩展,我们只需要这样写就行了,如下:

    复制代码
    object a = 1;
    if (a is int c) //这里,判断为int后就直接赋值给c
    {
      int d = c + 10;
      Console.WriteLine(d);
    }
    复制代码

    这样是不是很方便?特别是经常用反射的同志们..

    那么问题来了,挖掘机技术哪家强?!(咳咳,呸 开玩笑)

    其实是,如果有多种类型需要匹配,那怎么办?多个if else?当然没问题,不过,微软爸爸也提供了switch的新玩法,我们来看看,如下:

    我们定义一个Add的方法,以Object作为参数,返回动态类型

    复制代码
            static dynamic Add(object a)
            {
                dynamic data;
                switch (a)
                {
                    case int b:
                        data=b++;
                        break;
                    case string c:
                        data= c + "aaa";
                        break;
                    default:
                        data = null;
                        break;
                }
                return data;
            }
    复制代码

    下面运行,传入int类型:

    object a = 1;
    var data= Add(a);
    Console.WriteLine(data.GetType());
    Console.WriteLine(data);

    输出如图:

    我们传入String类型的参数,代码和输出如下:

    object a = "bbbb";
    var data= Add(a);
    Console.WriteLine(data.GetType());
    Console.WriteLine(data);

    通过如上代码,我们就可以体会到switch的新玩法是多么的顺畅和强大了.

    匹配模式的Case When筛选

    有的基友就要问了.既然我们可以在Switch里面匹配类型了,那我们能不能顺便筛选一下值?答案当然是肯定的.

    我们把上面的Switch代码改一下,如下:

    复制代码
                switch (a)
                {
                    case int b when b < 0:
                        data = b + 100;
                        break;
                    case int b:
                        data=b++;
                        break;
                    case string c:
                        data= c + "aaa";
                        break;
                    default:
                        data = null;
                        break;
                }
    复制代码

    在传入-1试试,看结果如下:

    4.ref locals and returns(局部变量引用返回)

    首先我们知道 ref关键字是将值传递变为引用传递

    那么我们先来看看ref locals(ref局部变量)

    列子代码如下:

    复制代码
           static  void Main(string[] args)
            {
    
                int x = 3;
                ref int x1 = ref x;  //注意这里,我们通过ref关键字 把x赋给了x1
                x1 = 2;
                Console.WriteLine($"改变后的变量 {nameof(x)} 值为: {x}");
                Console.ReadLine();
    
            }
    复制代码

    这段代码最终输出 "2"

    大家注意注释的部分,我们通过ref关键字把x赋给了x1,如果是值类型的传递,那么对x将毫无影响 还是输出3.

    好处不言而喻,在某些特定的场合,我们可以直接用ref来引用传递,减少了值传递所需要开辟的空间.

    接下来我们看看ref  returns (ref引用返回)

    这个功能其实是非常有用的,我们可以把值类型当作引用类型来进行return

    老规矩,我们举个栗子,代码如下:

    很简单的逻辑..获取指定数组的指定下标的值

    static ref int GetByIndex(int[] arr, int ix) => ref arr[ix];  //获取指定数组的指定下标

    我们编写测试代码如下:

                int[] arr = { 1, 2, 3, 4, 5 };
                ref int x = ref GetByIndex(arr, 2); //调用刚才的方法
                x = 99;
                Console.WriteLine($"数组arr[2]的值为: {arr[2]}");
                Console.ReadLine();

    我们通过ref返回引用类型,在重新赋值, arr数组中的值,相应也改变了.

    总结一下:ref关键字很早就存在了,但是他只能用于参数,这次C#7.0让他不仅仅只能作为参数传递,还能作为本地变量和返回值了

    5.Local Functions (局部函数)

    嗯,这个就有点颠覆..大家都知道,局部变量是指:只在特定过程或函数中可以访问的变量。

    那这个局部函数,顾名思义:只在特定的函数中可以访问的函数(妈蛋 好绕口)

    使用方法如下:

    复制代码
           public static void DoSomeing()
            {
                //调用Dosmeing2
                int data = Dosmeing2(100, 200);
                Console.WriteLine(data);
                //定义局部函数,Dosmeing2.
                int Dosmeing2(int a, int b)
                {
                   return a + b;
                }
            }
    复制代码

    呃,解释下来 大概就是在DoSomeing中定义了一个DoSomeing2的方法,..在前面调用了一下.(注:值得一提的是局部函数定义在方法的任何位置,都可以在方法内被调用,不用遵循逐行解析的方式)

    6.More expression-bodied members(更多的函数成员的表达式体)

    C#6.0中,提供了对于只有一条语句的方法体可以简写成表达式。

    如下:

    复制代码
            public void CreateCaCheContext() => new CaCheContext();
            //等价于下面的代码
            public void CreateCaCheContext()
            {
                new CaCheContext();
            } 
    复制代码

    但是,并不支持用于构造函数,析构函数,和属性访问器,那么C#7.0就支持了..代码如下:

    复制代码
    // 构造函数的表达式写法
    public CaCheContext(string label) => this.Label = label;
    
    // 析构函数的表达式写法
    ~CaCheContext() => Console.Error.WriteLine("Finalized!");
    
    private string label;
    
    // Get/Set属性访问器的表达式写法
    public string Label
    {
        get => label;
        set => this.label = value ?? "Default label";
    }
    复制代码

    7.throw Expressions (异常表达式)

    在C#7.0以前,我们想判断一个字符串是否为null,如果为null则抛除异常,我们需要这么写:

    复制代码
            public string IsNull()
            {
                string a = null;
                if (a == null)
                {
                    throw new Exception("异常了!");
                }
                return a;
            }
    复制代码

    这样,我们就很不方便,特别是在三元表达式 或者非空表达式中,都无法抛除这个异常,需要写if语句.

    那么我们在C#7.0中,可以这样:

            public string IsNull()
            {
                string a = null;
                return a ?? throw new Exception("异常了!");
            }

    8.Generalized async return types (通用异步返回类型)

    嗯,这个,怎么说呢,其实我异步用的较少,所以对这个感觉理解不深刻,还是觉得然并卵,在某些特定的情况下应该是有用的.

    我就直接翻译官方的原文了,实例代码也是官方的原文.

    异步方法必须返回 void,Task 或 Task<T>,这次加入了新的ValueTask<T>,来防止异步运行的结果在等待时已可用的情境下,对 Task<T> 进行分配。对于许多示例中设计缓冲的异步场景,这可以大大减少分配的数量并显著地提升性能。

    官方的实例展示的主要是意思是:一个数据,在已经缓存的情况下,可以使用ValueTask来返回异步或者同步2种方案

    复制代码
        public class CaCheContext
        {
            public ValueTask<int> CachedFunc()
            {
                return (cache) ? new ValueTask<int>(cacheResult) : new ValueTask<int>(loadCache());
            }
            private bool cache = false;
            private int cacheResult;
            private async Task<int> loadCache()
            {
                // simulate async work:
                await Task.Delay(5000);
                cache = true;
                cacheResult = 100;
                return cacheResult;
            }
        }
    复制代码

    调用的代码和结果如下:

    复制代码
            //main方法可不能用async修饰,所以用了委托.
            static  void Main(string[] args)
            {
                Action act = async () =>
                {
                    CaCheContext cc = new CaCheContext();
                    int data = await cc.CachedFunc();
                    Console.WriteLine(data);
                    int data2 = await cc.CachedFunc();
                    Console.WriteLine(data2);
                };
                // 调用委托  
                act();
                Console.Read();
    
            }
    复制代码

    上面的代码,我们连续调用了2次,第一次,等待了5秒出现结果.第二次则没有等待直接出现结果和预期的效果一致.

    9.Numeric literal syntax improvements(数值文字语法改进)

    这个就纯粹的是..为了好看了.

    在C#7.0中,允许数字中出现"_"这个分割符号.来提高可读性,举例如下:

    复制代码
                int a = 123_456;
                int b = 0xAB_CD_EF;
                int c = 123456;
                int d = 0xABCDEF;
                Console.WriteLine(a==c);
                Console.WriteLine(b==d);
                //如上代码会显示两个true,在数字中用"_"分隔符不会影响结果,只是为了提高可读性
       
    复制代码

    当然,既然是数字类型的分隔符,那么 decimalfloat 和 double  都是可以这样被分割的..

  • 相关阅读:
    JavaScript cookie详解
    Javascript数组的排序:sort()方法和reverse()方法
    javascript中write( ) 和 writeln( )的区别
    div做表格
    JS 盒模型 scrollLeft, scrollWidth, clientWidth, offsetWidth 详解
    Job for phpfpm.service failed because the control process exited with error code. See "systemctl status phpfpm.service" and "journalctl xe" for details.
    orm查询存在价格为空问题
    利用救援模式破解系统密码
    SSH服务拒绝了密码
    C# 调用 C++ DLL 中的委托,引发“对XXX::Invoke类型的已垃圾回收委托进行了回调”错误的解决办法
  • 原文地址:https://www.cnblogs.com/shiyh/p/8884630.html
Copyright © 2011-2022 走看看