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,在数字中用"_"分隔符不会影响结果,只是为了提高可读性