C# 6.0 版本包含许多可提升开发人员工做效率的功能。 这些功能的整体效果是让你编写的代码更简洁、更具可读性。 该语法不像许多常见作法那样繁琐。 能够更轻松地看出设计意图。 好好了解这些功能能够帮助你提升生产力,编写更具可读性的代码。 你能够更专一于功能,而不是语言的构造。html
本文的其他部分是对每一个功能的概述,并提供用于探索每一个功能的连接。 还能够在教程部分的 C# 6 交互式探索中探索这些功能。web
public string FirstName { get; } public string LastName { get; }
FirstName
和 LastName
属性只能在构造函数的主体中设置;express
尝试在另外一种普通方法中设置 LastName
会生成 CS0200
编译错误:api
此功能实现用于建立不可变类型的真正语言支持且使用更简洁和方便的自动属性语法。数组
public class Student { public string FirstName { get; } = "张"; public string LastName { get; private set; } = "传宁"; }
FirstName,LaseName
成员在声明它的位置处被初始化。 这样,就能更容易地仅执行一次初始化。 初始化是属性声明的一部分,可更轻松地将存储分配。安全
ToString()
一般是理想之选:
public override string ToString() => $"{LastName}, {FirstName}";
也能够将此语法用于只读属性:app
public string FullName => $"{FirstName} {LastName}";
将现有成员更改成 expression bodied 成员是二进制兼容的更改。async
using static System.Math;
Math 不包含任何实例方法。 还可使用 using static
为具备静态和实例方法的类导入类的静态方法。 最有用的示例之一是 String:ide
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 静态指令》
.
替换为 ?.
:
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);
确保左侧只计算一次,这使得你能够在 ?.
的左侧使用任何表达式(包括方法调用)。
$
做为字符串的开头,并使用 {
和 }
之间的表达式代替序号:
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"));
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("301")) // 若是判断的逻辑较多,建议使用该方式。 { return "Site Moved"; } }
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))); } } }
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("301")) { 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 子句,避免引入新的异常。
private Dictionary<int, string> messages = new Dictionary<int, string> { { 404, "Page not Found"}, { 302, "Page moved, but left a forwarding address."}, { 500, "The web server can't come out to play today."} };
能够将集合初始值设定项与 Dictionary<TKey,TValue> 集合和其余类型一块儿使用,在这种状况下,可访问的 Add
方法接受多个参数。 新语法支持使用索引分配到集合中:
private Dictionary<int, string> webErrors = new Dictionary<int, string> { [404] = "Page not Found", [302] = "Page moved, but left a forwarding address.", [500] = "The web server can't come out to play today." };
此功能意味着,可使用与多个版本中已有的序列容器语法相似的语法初始化关联容器。
Add
方法使用扩展方法。 添加此功能的目的是进行 Visual Basic 的奇偶校验。 若是自定义集合类的方法具备经过语义方式添加新项的名称,则此功能很是有用。
static Task DoThings() { return Task.FromResult(0); }
在早期版本的 C# 中,使用方法组语法调用该方法将失败:
Task.Run(DoThings);
早期的编译器没法正确区分 Task.Run(Action)
和 Task.Run(Func<Task>())
。 在早期版本中,须要使用 lambda 表达式做为参数:
Task.Run(() => DoThings());
C# 6 编译器正确地肯定 Task.Run(Func<Task>())
是更好的选择。
-deterministic
选项指示编译器为同一源文件的后续编译生成彻底相同的输出程序集。
默认状况下,每一个编译都生成惟一的输出内容。 编译器添加一个时间戳和一个随机生成的 GUID。 若是想按字节比较输出以确保各项生成之间的一致性,请使用此选项。
有关详细信息,请参阅 -deterministic 编译器选项文章。