Bootstrap
随着vs的更新,vs2017中已经是c#7.0。c#7.0又有哪些新特性呢?接下来简单介绍一下。

1、 out-variables

以往,我们在使用out关键字,返回执行结果时,需要在调用之前,声明一个变量。如下:

 static void Main(string[] args)
        {
            string value = "1314";
            int result = 0;
            int.TryParse(value, out result);
            Console.WriteLine(result);
        }

在c#7.0中,我们可以先不申明变量,而是在out时,声明变量,在之后可以直接使用此变量,如下:

 static void Main(string[] args)
        {
            string value = "1314";
            int.TryParse(value, out int result);
            Console.WriteLine(result);
        }

2、Tuples(元组)

以前,c#4.0时方法要返回多个值,还有个解决方案是元组,类似代码如下:

 static void Main(string[] args)
        {
            var results = GetResults();
            Console.WriteLine(results.Item1);
            Console.WriteLine(results.Item2);
            Console.WriteLine(results.Item3);
        }
        static Tuple GetResults()
        {
            return new Tuple("success", 1, "error");
        }

上面代码是一个方法返回3个值,但是调用时的属性名比较奇怪(item1,item2,item3),并且代码也不够简洁。

而在c#7.0中提供了更优雅的方案(注意:需要通过nuget引用System.ValueTuple)如下:

 static void Main(string[] args)
        {
            var results = GetResults();
            Console.WriteLine(results.status);
            Console.WriteLine(results.code);
            Console.WriteLine(results.exception);
        }
static (string status,int code,string exception) GetResults()
        {
            return ("success", 1, "error");
        }

3、 Pattern Matching(匹配模式)

在c#7.0之前,要判断一个object变量类型,并执行一些操作,类似如下代码:

  static void Main(string[] args)
        {
            object val = 1;
            if (val is int)
            {
                Console.WriteLine(Convert.ToInt32(val) + 1);//需要将object类型转换成int,然后加+1
            }
        }

在c#7.0中,则要简洁方便许多,只要这样写行了,如下:

static void Main(string[] args)
        {
            object val = 1;
            if (val is int p)//直接声明变量,并赋值给这个变量
            {
                Console.WriteLine(p + 1);
            }
        }

如果有多个类型,不仅可以用多个 if else,还可以用有新语法的swich,我们来看一下,如下:

我们定义一个方法,参数传object类型,返回动态类型

 static dynamic GetResult(object val)
        {
            dynamic result;
            switch (val)
            {
                case int code:
                    result = code;
                    break;
                case string status:
                    result = status;
                    break;
                default:
                    result = null;
                    break;
            }
            return result;
        }

通过上面的代码,可以看到swich的新语法,还是很强大的。

匹配模式的Case When筛选

如果还想对匹配到的值进行筛选,就要用when来实现了,上面代码修改如下:

        static dynamic GetResult(object val)
        {
            dynamic result;
            switch (val)
            {
                case int code when code<0:
                    result = 0;
                    break;
                case int code:
                    result = code;
                    break;
                case string status:
                    result = status;
                    break;
                default:
                    result = null;
                    break;
            }
            return result;
        }

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

我们都知道,ref 是将值类型变量改为引用传递,在C#7.0之前 ref只能用在方法参数里,但C#7.0中可以用在返回值和局部变量中。我们先看一下ref locals(ref局部变量),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();
        }


这段代码最终输出 “改变后的变量 x 值为: 2”

注意x1=2这段代码,如果是值传递,那么x最终输出的应该是3,改变x1对x应该没有影响的。

在某些情况下,可以通过ref引用传递,可以节省内存空间

在看一下ref returns(ref 引用返回)

这个功能还是挺有用的,可以把值类型当作引用类型返回。我们看一下代码:

  static ref int GetByIndex(int[] arr, int ix) => ref arr[ix];
  static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3, 4, 5 };
            ref int x = ref GetByIndex(arr, 2); 
            x = 102;
            Console.WriteLine($"数组arr[2]的值为: {arr[2]}");
            Console.ReadKey();
            
        }

这段代码最终输出 “数组arr[2]的值为: 102”

说明数组中的值改变了。

5、Local Functions (局部方法)

顾名思义,其实和局部变量差不多,也是在方法内部能访问和调用,出了方法就访问不到了。

具体怎么使用,我们在看一下如下代码:

 static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3, 4, 5 };
            ref int x = ref GetByIndex(arr, 2); //调用刚才的方法
            x = 102;
            Console.WriteLine($"数组arr[2]的值为: {arr[2]}");
            Console.ReadKey();
            ref int GetByIndex(int[] arry, int ix) => ref arry[ix];//局部方法
        }


上面的代码,只是把上个例子中用到的GetByIndex方法,写在Main方法内部。这就是局部方法。

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

c#6.0中可以把只有一条代码的写成Lambda表达式,如下:

 ref int GetByIndex(int[] arry, int ix) => ref arry[ix];

等价于下面的代码 

ref int GetByIndex(int[] arry,int ix)
{
       return arry[ix];
}

但是不支持用于构造函数,析构函数,和属性访问器

而在c#7.0中就可以支持了,代码如下:

public class MyClass
{
        private string name;
        public string Name {
            get => this.name;
            set => this.name = value;
        }
        ~MyClass() => Console.WriteLine("~MyClass");
        public MyClass() => this.Name = "MyClass";
}    

7、 throw Expressions (异常表达式)

c#7.0可以这样来判断变量是否为null,并且抛出异常,代码如下:

  static void Main(string[] args)
        {
            string str = null;
            Console.WriteLine(str ?? throw new NullReferenceException("str is null"));
        }

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

异步方法必须返回 void,Task 或 Task ,c#7.0中加入了ValueTask,使用它必须nuget引用 System.Threading.Tasks.Extensions。

之前的返回类型,异步任务都必须执行之后,才能得到结果,而ValueTask可以判断是否有缓存,缓存存在就直接返回结果 ,不存在就执行异步

任务。代码如下:

public class CaCheContext
    {
        public ValueTask CachedFunc()
        {
            return (cache) ? new ValueTask(cacheResult) : new ValueTask(loadCache());
        }
        private bool cache = false;
        private int cacheResult;
        private async Task 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();
        }

如果没有ValueTask,则第二次执行还要等待5秒,而有了ValueTask可以直接返回结果。

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

在C#7.0中,允许数字中出现"_"进行分隔,而不影响原本的数值,主要是为了方便查看。提高可读性,举例如下:

            int a = 123_456;
            int b = 0xABC_DEF;
            int c = 123456;
            int d = 0xABCDEF;
            Console.WriteLine(a==c);
            Console.WriteLine(b==d);

            //如上代码会显示两个true,在数字中用"_"分隔符不会影响结果,只是为了提高可读性