C# 6 中的新增功能

只读自动属性

只读自动属性 提供了更简洁的语法来创建不可变类型。

1
2
public string FirstName { get; }
public string LastName { get; }

FirstName 和 LastName 属性只能在同一个类的构造函数的主体中设置:

1
2
3
4
5
6
7
public Student(string firstName, string lastName)
{
if (IsNullOrWhiteSpace(lastName))
throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
FirstName = firstName;
LastName = lastName;
}

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

1
2
3
4
5
6
7
8
9
10
public class Student
{
public string LastName { get; }

public void ChangeName(string newLastName)
{
// Generates CS0200: Property or indexer cannot be assigned to -- it is read only
LastName = newLastName;
}
}

自动属性初始化

自动属性初始值设定项 可让你在属性声明中声明自动属性的初始值。

1
public ICollection<double> Grades { get; } = new List<double>();

Grades 成员在声明它的位置处被初始化。 这样,就能更容易地仅执行一次初始化。 初始化是属性声明的一部分,可更轻松地将存储分配等同于 Student 对象的公用接口。

Expression-bodied 函数成员

编写的许多成员是可以作为单个表达式的单个语句。 改为编写 expression-bodied 成员。 这适用于方法和只读属性。

1
2
3
4
5
// 方法
public override string ToString() => $"{LastName}, {FirstName}";

// 也可以将此语法用于只读属性:
public string FullName => $"{FirstName} {LastName}";

using static

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

1
2
3
using static System.Math;
using static System.Linq.Enumerable;
using static System.String;

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

在 LINQ 查询中会经常看到这种情况。 可以通过导入 Enumerable 或 Queryable 来导入 LINQ 模式。

using static 语法导入一个类型,然后就可以在其全局作用域范围内(当前文件内)使用它可以访问(遵循访问修饰符的限定)类型的静态成员了,需要注意的几点是:

  • 导入的成员签名和现有的成员签名相同时,使用现有的成员。
  • 导入的成员之间出现成员签名相同的情况,使用的时候会编译不通过,需要一处一个 using static 才可,或者改为正常的调用方式。
  • class,struct,emun 类型可以使用 using static 导入。
  • 静态属性,字段,事件等等,,,静态成员均可依靠 using static 省略类型前缀。
  • 扩展方法也可以使用 using static,但是需要按照实例方法的调用方式来使用。

参考:[C#6] 1-using static

Null 条件运算符

1
2
3
4
5
6
7
8
9
// 如果 Person 对象是 null,则将变量 first 赋值为 null。 否则,将 FirstName 属性的值分配给该变量。
var first = person?.FirstName;

// 无论 person 的值是什么,以下表达式均返回 string。
// 通常,将此构造与 null coalescing (null 合并) 运算符一起使用
first = person?.FirstName ?? "Unspecified";

// 调用该委托
this.SomethingHappened?.Invoke(this, eventArgs);

字符串内插

关键符号:${}

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

异常筛选器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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";
}
}
}

nameof 表达式

nameof 表达式的计算结果为符号的名称。

1
2
if (IsNullOrWhiteSpace(lastName))
throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

Catch 和 Finally 块中的 Await

C# 5 对于可放置 await 表达式的位置有若干限制。 使用 C# 6,现在可以在 catch 或 finally 表达式中使用 await。
鉴于此行为,建议仔细编写 catch 和 finally 子句,避免引入新的异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
}
}

使用索引器初始化关联集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 原来语法
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."}
};

// 新语法支持使用索引分配到集合中:
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."
};

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

参考:

C# 6 中的新增功能