目录
C#7.0语言增加了许多的新功能,促使专注于数据消费,简化代码和性能。
输出(out)变量
一个方法有多个返回值时,返回值类型相同可以返回一个数组。
返回值类型不同时可以使用out
out参数:返回值多个,不限类型
注意事项:
- 调用方法之前,对out参数传递的变量只需声明,可以赋值也可以不赋值,赋值也会在方法中被覆盖掉
- 使用out参数传递变量时,必须在方法内为其赋值,否则return的返回值没有意义
- 方法的参数使用out修饰时,调用该方法时也要加上out关键字
- 使用out修饰的变量不需要return
using System;
namespace test
{
class Program
{
static void Main(string[] args)
{
int x;
string y;
Program.Method(out x,out y);
Console.WriteLine(x+y);
Console.ReadLine();
}
public static void Method(out int x,out string y)
{
x = 10;
y = "hello";
}
}
}
结果:
在C#7.0,我们增加了Out变量,作为out参数传递加入了声明变量权,如下。
using System;
namespace test
{
class Program
{
static void Main(string[] args)
{
//int x;
//string y;
Program.Method(out int x,out string y);
Console.WriteLine(x+y);
Console.ReadLine();
}
public static void Method(out int x,out string y)
{
x = 10;
y = "hello";
}
}
}
也即省略了声明变量的过程,运行结果同上。
模式匹配
C# 7.0 引入了模式的概念,抽象地说,这是一种语法成分可以用来测试一个值是否有一个一定的“形”以及在它起作用时从值里面获取到的额外信息。
C#从7.0开始,陆陆续续推出了各种模式匹配,模式是一种特殊的表达式,通过判断给定的值是否满足此表达式而返回true或者false,它就类似于正则表达式的作用。
部分使用方式如下。
Object value = "";
if (value is string)
{
Console.WriteLine("value is String");
}
//输出:value is String
Object value = "String";
if (value is string str)
{
Console.WriteLine($"value is {str}");
}
//输出:value is String
Object value = "String";
if (value is "String")
{
Console.WriteLine($"value is String");
}
//输出:value is String
Object value = null;
if (value is null)
{
Console.WriteLine($"value is null");
}
//输出:value is null
元组
这是常见的希望从一个方法返回多个值的做法。目前可用的选项不是最佳的:
- Out 参数。使用笨拙(即便有上面描述到的提升),它们不使用异步的方法运行。
- System.Tuple<...> 返回类型。使用累赘并且需要一个元组对象的分配。
- 为每个方法定制传输类型:大量的代码为了类型开销的目的仅是临时收集一些值
- 匿名类型通过返回一个 dynamic 返回类型。高性能开销并且没有静态类型检查。
using System;
namespace test
{
class Program
{
static void Main(string[] args)
{
var data = new Tuple<string, int, char>("a",123,'c');
Console.WriteLine(data.Item1);
Console.WriteLine(data.Item2);
Console.WriteLine(data.Item3);
Console.ReadLine();
}
}
}
结果:
在C#7.0中,微软提供了更优雅的方案。
using System;
namespace test
{
class Program
{
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匿名来获取,那么就可以通过解构的方式获取。
using System;
namespace test
{
class Program
{
static void Main(string[] args)
{
(string a,int b,char c) = new Tuple<string, int, char>("a", 123, 'c');
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.ReadLine();
}
}
}
结果如下:
也可以将数据类型放到括号外面如下。
var ( a, b, c) = new Tuple<string, int, char>("a", 123, 'c');
也可以通过解构赋值一个现有变量。
不仅仅元组可以被解构,任何类型都可以被解构,只要有一个对应的(实体或者扩展)解构方法即可。
局部函数
局部变量是指:只在特定过程或函数中可以访问的变量。
局部函数是指:只在特定的函数中可以访问的函数。
using System;
namespace test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(sum(100,200));
Console.ReadLine();
int sum(int x,int y)
{
return x + y;
}
}
}
}
结果:
方法实现迭代器通常需要严格检查调用时非迭代器封装方法。(迭代器本身没有运行,只到调用MoveNext 才会运行)。局部函数在这种情况下是完美的。
如果迭代器是一个私有方法的下一个过滤器,它将有可能被其他成员不小心使用(没有参数检查)。此外,作为过滤器,它将需要采取所有的相同的参数,而不是指定域内的参数。
Literal 改进
在C#7.0中,允许数字中出现"_"这个分割符号,来提高可读性,例如:
static void Main(string[] args)
{
int a = 123_456;
Console.WriteLine(a);//输出结果是123456
Console.ReadLine();
}
因为是数字类型的,因此,decimal,float,double都可以这样分割。
static void Main(string[] args)
{
int a = 123_456;
float b = 123.456_789f;
double c = 123.456_789;
decimal d = 123.456_789M;
int e = 0xABC_DEF;
Console.WriteLine(a);
Console.WriteLine(b);
Console.WriteLine(c);
Console.WriteLine(d);
Console.WriteLine(e);
Console.ReadLine();
}
在数字中使用“_”分隔符不会影响结果,只是为了提高可读性。
Ref 本地和返回
ref关键字是将值传递变为引用传递
ref locals(ref局部变量,也即本地)
static void Main(string[] args)
{
int x = 1;
ref int y = ref x;//将x的引用(也就是地址)传给y
y = 2;
Console.WriteLine($"x={x}");//输出结果为x=2
Console.ReadLine();
}
这里通过ref关键字把x赋给了y,在给y赋值,如果是值类型的传递,那么x将不会改变。
在某些特定的场合,我们可以直接用ref来引用传递,减少了值传递所需要开辟的空间。
ref returns (ref引用返回)
这个方式可以把值类型当作引用类型来进行return
static void Main(string[] args)
{
int[] arr = { 1, 2, 3, 4, 5, 6 };
ref int x = ref GetArrIndex(arr, 2);
x = 123;
Console.WriteLine($"arr[2]={arr[2]}");
Console.ReadLine();
ref int GetArrIndex(int[] arreray,int index)
{
return ref arreray[index];
}
}
结果:
通过ref返回引用类型,在重新赋值,arr数组中的值,相应也改变了。
注意:ref关键字很早就存在了,但是他只能用于参数,这次C#7.0让他不仅仅只能作为参数传递,还能作为本地变量和返回值了
广义异步返回类型
异步方法必须返回 void,Task 或 Task<T>,这次加入了新的ValueTask<T>,来防止异步运行的结果在等待时已可用的情境下,对 Task<T> 进行分配。对于许多示例中设计缓冲的异步场景,这可以大大减少分配的数量并显著地提升性能。
也即:在已经缓存的情况下,可以使用ValueTask来返回异步或者同步2种方案
更多 Expression-bodied 方法
C#6.0中,提供了对于只有一条语句的方法体可以简写成表达式。
public void Show() => new MyTest();
//等价于下面的代码
public void Show()
{
new MyTest();
}
但是,并不支持用于构造函数,析构函数,和属性访问器,那么C#7.0就支持了。
class MyTest
{
//get set 写法
public string name
{
get => name;
set => this.name=value ?? "name is null";
}
//构造函数表达式写法
public MyTest(string name) => this.name = name;
//析构函数表达式写法
~MyTest() => Console.WriteLine($"name={this.name}");
}