C# 6.0 版本包含许多可提高开发人员工作效率的功能。 这些功能的总体效果是让你编写的代码更简洁、更具可读性。 该语法不像许多常见做法那样繁琐。 可以更轻松地看出设计意图。 好好了解这些功能可以帮助你提高生产力,编写更具可读性的代码。 你可以更专注于功能,而不是语言的构造。

本文的其余部分是对每个功能的概述,并提供用于探索每个功能的链接。 还可以在教程部分的 C# 6 交互式探索中探索这些功能。

01 只读自动属性
只读自动属性提供了更简洁的语法来创建不可变类型。 你声明仅具有 get 访问器的自动属性:
public string FirstName { get; }
public string LastName { get; }

FirstName 和 LastName 属性只能在构造函数的主体中设置;

尝试在另一种普通方法中设置 LastName 会生成 CS0200 编译错误:

此功能实现用于创建不可变类型的真正语言支持且使用更简洁和方便的自动属性语法。

02 自动属性初始化表达式
自动属性初始值设定项可让你在属性声明中声明自动属性的初始值。
   public class Student
{
public string FirstName { get; } = "张";
public string LastName { get; private set; } = "传宁";
}

FirstName,LaseName 成员在声明它的位置处被初始化。 这样,就能更容易地仅执行一次初始化。 初始化是属性声明的一部分,可更轻松地将存储分配。

03 Expression-bodied(正文表达式) 函数成员
你编写的许多成员是可以作为单个表达式的单个语句。 改为编写 expression-bodied 成员。这适用于方法和只读属性。 例如,重写 ToString() 通常是理想之选:
public override string ToString() => $"{LastName}, {FirstName}";

也可以将此语法用于只读属性:

public string FullName => $"{FirstName} {LastName}";

将现有成员更改为 expression bodied 成员是二进制兼容的更改

04 静态导入 using static
using static 增强功能可用于导入单个类的静态方法。 指定要使用的类:
using static System.Math;

Math 不包含任何实例方法。 还可以使用 using static 为具有静态和实例方法的类导入类的静态方法。 最有用的示例之一是 String

using static System.String;

在 using static 语句中必须使用完全限定的类名 System.String。 而不能使用 string 关键字。

从 static using 语句导入时,仅在使用扩展方法调用语法调用扩展方法时,扩展方法才在范围内。 作为静态方法调用时,扩展方法不在范围内。 你在 LINQ 查询中会经常看到这种情况。 可以通过导入 Enumerable 或 Queryable 来导入 LINQ 模式。

using static System.Linq.Enumerable;

通常使用扩展方法调用表达式调用扩展方法。 在使用静态方法调用语法对其进行调用的罕见情况下,添加类名称可以解决歧义。

static using 指令还可以导入任何嵌套的类型。 可以引用任何嵌套的类型,而无需限定。

看下面的一个具体事例:

旧语法:

using System;

namespace Demo002_NF46_CS60
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello world");
}
}
}

引入 using static 语法:

using static System.Console;

namespace Demo002_NF46_CS60
{
class Program
{
static void Main(string[] args)
{
WriteLine("Hello world");
}
}
}

关于using static 的更具体的信息,请参考《using 静态指令》

05 Null 条件运算符
Null 条件运算符使 null 检查更轻松、更流畅。 将成员访问 . 替换为 ?.
var first = person?.FirstName;

在前面的示例中,如果 Person 对象是 null,则将变量 first 赋值为 null。 否则,将 FirstName 属性的值分配给该变量。 最重要的是?. 意味着当 person 变量为 null 时,此行代码不会生成 NullReferenceException。 它会短路并返回 null。 还可以将 null 条件运算符用于数组或索引器访问。 将索引表达式中的 [] 替换为 ?[]

当 FirstName 为 null 时,变量 firstName 为 null,打印输出时不报错:

无论 person 的值是什么,以下表达式均返回 string。 通常,将此构造与“null 合并”运算符一起使用,以在其中一个属性为 null 时分配默认值。 表达式短路时,键入返回的 null值以匹配整个表达式。

first = person?.FirstName ?? "Unspecified";

还可以将 ?. 用于有条件地调用方法。 具有 null 条件运算符的成员函数的最常见用法是用于安全地调用可能为 null 的委托(或事件处理程序)。 通过使用 ?. 运算符调用该委托的 Invoke 方法来访问成员。 可以在委托模式一文中看到示例。

?. 运算符的规则确保运算符的左侧仅计算一次。 它支持许多语法,包括使用事件处理程序的以下示例:

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

确保左侧只计算一次,这使得你可以在 ?. 的左侧使用任何表达式(包括方法调用)。

06 字符串内插
使用 C# 6,新的字符串内插功能可以在字符串中嵌入表达式。 使用 $ 作为字符串的开头,并使用 { 和 } 之间的表达式代替序号:
public string FullName => $"{FirstName} {LastName}";

本示例使用替代表达式的属性。 可以使用任何表达式。 例如,可以在内插过程中计算学生的成绩平均值:

public string GetGradePointPercentage() => $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

上一行代码将 Grades.Average() 的值格式设置为具有两位小数的浮点数。

通常,可能需要使用特定区域性设置生成的字符串的格式。 请利用通过字符串内插生成的对象可以隐式转换为 System.FormattableString 这一事实。 FormattableString 实例包含组合格式字符串,以及在将其转换为字符串之前评估表达式的结果。 在设置字符串的格式时,可以使用 FormattableString.ToString(IFormatProvider) 方法指定区域性。 下面的示例使用德语 (de-DE) 区域性生成字符串。 (德语区域性默认使用“,”字符作为小数分隔符,使用“.”字符作为千位分隔符。)

FormattableString str = $"Average grade is {s.Grades.Average()}";
var gradeStr = str.ToString(new System.Globalization.CultureInfo("de-DE"));

要开始使用字符串内插,请参阅 字符串内插 一文和  C# 中字符串内插、 符合格式设置 教程。

07 异常筛选器
“异常筛选器”是确定何时应该应用给定的 catch 子句的子句。 如果用于异常筛选器的表达式计算结果为 true,则 catch 子句将对异常执行正常处理。 如果表达式计算结果为 false,则将跳过 catch 子句。 一种用途是检查有关异常的信息,以确定 catch 子句是否可以处理该异常:
public static async Task<string> MakeRequest()
{
WebRequestHandler webRequestHandler = new WebRequestHandler();
webRequestHandler.AllowAutoRedirect = false;
using (HttpClient client = new HttpClient(webRequestHandler))
{
var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
try
{
var responseText = await stringTask;
return responseText;
}
catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
{
return "Site Moved";
}
}
}

相当于

 catch (System.Net.Http.HttpRequestException e)
{
if(e.Message.Contains("")) // 如果判断的逻辑较多,建议使用该方式。
{
return "Site Moved";
}
}
08 nameof 表达式
nameof 表达式的计算结果为符号的名称。 每当需要变量、属性或成员字段的名称时,这是让工具正常运行的好办法。 nameof 的其中一个最常见的用途是提供引起异常的符号的名称:
if (IsNullOrWhiteSpace(lastName))
{
throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
}

另一个用途是用于实现 INotifyPropertyChanged 接口的基于 XAML 的应用程序:

private string lastName;
public string LastName
{
get { return lastName; }
set
{
if (value != lastName)
{
lastName = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LastName)));
}
}
}
09 Catch 和 Finally 块中的 Await
C# 5 对于可放置 await 表达式的位置有若干限制。 使用 C# 6,现在可以在 catch 或 finally 表达式中使用 await。 这通常用于日志记录方案:
public static async Task<string> MakeRequestAndLogFailures()
{
await logMethodEntrance();
var client = new System.Net.Http.HttpClient();
var streamTask = client.GetStringAsync("https://localHost:10000");
try
{
var responseText = await streamTask;
return responseText;
} catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains(""))
{
await logError("Recovered from redirect", e);
return "Site Moved";
}
finally
{
await logMethodExit();
client.Dispose();
}
}

在 catch 和 finally 子句中添加 await 支持的实现细节可确保该行为与同步代码的行为一致。 当在 catch 或 finally 子句中执行的代码引发异常时,执行将在下一个外层块中查找合适的 catch 子句。 如果存在当前异常,则该异常将丢失。 catch 和 finally 子句中的 awaited 表达式也会发生同样的情况:搜索合适的 catch,并且当前异常(如果有)将丢失。

鉴于此行为,建议仔细编写 catch 和 finally 子句,避免引入新的异常。

10 使用索引器初始化关联集合
索引初始值设定项是提高集合初始值设定项与索引用途一致性的两个功能之一。 在早期版本的 C# 中,可以将集合初始值设定项用于序列样式集合,包括在键值对周围添加括号而得到 Dictionary<TKey,TValue>
private Dictionary<int, string> messages = new Dictionary<int, string>
{
{ , "Page not Found"},
{ , "Page moved, but left a forwarding address."},
{ , "The web server can't come out to play today."}
};

可以将集合初始值设定项与 Dictionary<TKey,TValue> 集合和其他类型一起使用,在这种情况下,可访问的 Add 方法接受多个参数。 新语法支持使用索引分配到集合中:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
[] = "Page not Found",
[] = "Page moved, but left a forwarding address.",
[] = "The web server can't come out to play today."
};

此功能意味着,可以使用与多个版本中已有的序列容器语法类似的语法初始化关联容器。

11 集合初始值设定项中的扩展 Add 方法
使集合初始化更容易的另一个功能是对 Add 方法使用扩展方法。 添加此功能的目的是进行 Visual Basic 的奇偶校验。 如果自定义集合类的方法具有通过语义方式添加新项的名称,则此功能非常有用。
12 改进了重载解析
 在以前的一些构造中,以前版本的 C# 编译器可能会发现涉及 lambda 表达式的一些方法不明确。 请考虑此方法:
static Task DoThings()
{
return Task.FromResult();
}

在早期版本的 C# 中,使用方法组语法调用该方法将失败:

Task.Run(DoThings);

早期的编译器无法正确区分 Task.Run(Action) 和 Task.Run(Func<Task>())。 在早期版本中,需要使用 lambda 表达式作为参数:

Task.Run(() => DoThings());

C# 6 编译器正确地确定 Task.Run(Func<Task>()) 是更好的选择。

确定性的编译器选项

-deterministic 选项指示编译器为同一源文件的后续编译生成完全相同的输出程序集。

默认情况下,每个编译都生成唯一的输出内容。 编译器添加一个时间戳和一个随机生成的 GUID。 如果想按字节比较输出以确保各项生成之间的一致性,请使用此选项。

有关详细信息,请参阅 -deterministic 编译器选项文章。

C#6.0 新增功能的更多相关文章

  1. C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点

    C#基础拾遗系列之二:使用ILSpy探索C#7.0新增功能点   第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它 ...

  2. C#2.0新增功能06 协变和逆变

    连载目录    [已更新最新开发文章,点击查看详细] 在 C# 中,协变和逆变能够实现数组类型.委托类型和泛型类型参数的隐式引用转换. 协变保留分配兼容性,逆变则与之相反. 以下代码演示分配兼容性.协 ...

  3. C#基础拾遗系列之二:C#7.0新增功能点

    第一部分: C#是一种通用的,类型安全的,面向对象的编程语言.有如下特点: (1)面向对象:c# 是面向对象的范例的一个丰富实现, 它包括封装.继承和多态性.C#面向对象的行为包括: 统一的类型系统 ...

  4. C#7.0新增功能点

    原文地址:  https://www.cnblogs.com/runningsmallguo/p/8972678.html 第二部分:C#7.0新增的功能 (1)数字字面量的提升: C#7中的数字文字 ...

  5. C#2.0新增功能01 分布类与分部方法

    连载目录    [已更新最新开发文章,点击查看详细] 分部类型 拆分一个类.一个结构.一个接口或一个方法的定义到两个或更多的文件中, 每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组 ...

  6. 说说C# 8.0 新增功能Index和Range的^0是什么?

    前言 在<C# 8.0 中使用 Index 和 Range>这篇中有人提出^0是什么意思?处于好奇就去试了,结果抛出异常.查看官方文档说^0索引与 sequence[sequence.Le ...

  7. Android 7.0 新增功能和api

    Android 7.0 Nougat 为用户和开发者引入多种新功能.本文重点介绍面向开发者的新功能. 请务必查阅 Android 7.0 行为变更以了解平台变更可能影响您的应用的领域. 要详细了解 A ...

  8. Xcode 9.0 新增功能大全

    Xcode是用于为Apple TV,Apple Watch,iPad,iPhone和Mac创建应用程序的完整开发人员工具集.Xcode开发环境采用tvOS SDK,watchOS SDK,iOS SD ...

  9. C#6.0新增功能

    C# 6.0 版本包含许多可提高开发人员工作效率的功能. 此版本中的功能包括: 只读自动属性: 可以创建只能在构造函数中设置的只读自动属性. 自动属性初始值设定项: 可以编写初始化表达式来设置自动属性 ...

  10. C#8.0 新增功能

    连载目录    [已更新最新开发文章,点击查看详细] C#8.0提供了许多增强功能 01 Readonly 成员 可将 readonly 修饰符应用于结构的任何成员. 它指示该成员不会修改状态. 这比 ...

随机推荐

  1. Linux 桌面玩家指南:19. 深入理解 JavaScript,及其开发调试工具

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  2. delphi7 xml通用解析转换为stringgrid

    对于有n多记录的xml,可以填充到stringgrid中 其中 vkeynode 为 xml中 重复节点 function CommonAnalyzeXml(vxml,vkeynode: string ...

  3. JS基本类型特性总结

    本文代码测试环境: win7 32位,chrome 版本如下: 一,JS基本数据类型:Undefined, Null, Boolean, Number, String, Object六种. 1,Und ...

  4. java多线程之Lock/Condition简介

    在java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5中增加的ReentrantLock也能实现同样的效果,并且功能更加强大. 比如具有嗅探锁定,多路分支通知 ...

  5. 查看linux系统时间和时区

    参考地址:http://lidao.blog.51cto.com/ 一.使用date命令查看系统时间 [root@benbang ~]# date -R Tue, 01 Aug 2017 15:43: ...

  6. Loadrunner做性能测试的主要步骤

    Loadrunner做性能测试的主要步骤: Loadrunner将性能测试过程分为计划测试.测试设计.创建VU脚本.创建测试场景.运行测试场景和分析结果6个步骤. 1) 计划测试:主要进行测试需求的收 ...

  7. spring 5.x 系列第4篇 —— spring AOP (代码配置方式)

    文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 2.5 切面执行顺 ...

  8. VsCode 常用快捷键、debug菜单、debug插件

    常用快捷键emmet                   百度emmet即可知  Ctrl + P            转到文件Ctrl+鼠标左键不松手     预览代码Ctrl+鼠标左键松手    ...

  9. 【Netty4】深入学习Netty

    Netty is an asynchronous event-driven network application framework  for rapid development of mainta ...

  10. SQL经典练习题50--mysql

    --1.学生表 Student(Sid,Sname,Sage,Ssex)? --Sid 学生编号,Sname 学生姓名,Sage 出生年月,Ssex 学生性别 --2.课程表? Course(Cid, ...