C# 6.0 新特性解析:让代码更简洁
2016.03.25
C#
3 min
1.3k 字
// 目录 · contents
VS 2015 发布后,C# 6.0
随之而来。这个版本没有大的架构性改变(async/await 是 5.0
的事),主要是一批”语法糖”——让常见模式写起来更简洁。升级项目之后我把这些新特性挨个试了一遍,记录哪些真的有用、哪些只是噱头。
1. Null 条件运算符 ?.
这个是 6.0 里我最喜欢的特性,彻底消灭了大量防御性 null 检查代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| string city = null; if (user != null && user.Address != null) { city = user.Address.City; }
string city = user?.Address?.City;
string city = user?.Address?.City ?? "未知城市";
int? length = user?.Name?.Length;
string first = list?[0];
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
2. 字符串插值 $""
告别 string.Format 的索引地狱:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| string name = "Alice"; int age = 30;
string msg1 = string.Format("用户 {0},年龄 {1}", name, age);
string msg2 = $"用户 {name},年龄 {age}";
string msg3 = $"用户 {name.ToUpper()},年龄 {age},成年人:{age >= 18}";
decimal price = 1234.5m; string msg4 = $"价格:{price:C2}"; string msg5 = $"日期:{DateTime.Now:yyyy-MM-dd}";
string sql = $@" SELECT * FROM Users WHERE Name = '{name}' AND Age > {age} ";
|
注意:字符串插值编译后会转换为
string.Format,性能与之相当。如果在循环中拼接大量字符串,仍应使用
StringBuilder。
3.
表达式体成员(Expression-bodied Members)
单行方法和属性不再需要大括号:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public string FullName { get { return $"{FirstName} {LastName}"; } }
public string GetGreeting(string name) { return $"Hello, {name}!"; }
public string FullName => $"{FirstName} {LastName}"; public string GetGreeting(string name) => $"Hello, {name}!";
public static Vector operator +(Vector a, Vector b) => new Vector(a.X + b.X, a.Y + b.Y);
|
对于 DTO 和简单的领域对象,这个特性让代码行数减少了约
30%,可读性也提升了。
4. nameof 运算符
告别硬编码的字符串属性名:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public string Name { set { _name = value; OnPropertyChanged("Name"); } }
public string Name { set { _name = value; OnPropertyChanged(nameof(Name)); } }
public void SetAge(int age) { if (age < 0) throw new ArgumentOutOfRangeException(nameof(age), "年龄不能为负数"); }
Log.Info($"方法 {nameof(GetUsers)} 开始执行");
|
nameof
在编译期求值,没有运行时开销,也不会被混淆工具处理掉(字符串字面量会被混淆)。
5. 字典初始化语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var config = new Dictionary<string, string> { { "host", "localhost" }, { "port", "5432" }, { "db", "myapp" } };
var config = new Dictionary<string, string> { ["host"] = "localhost", ["port"] = "5432", ["db"] = "myapp" };
|
6. 异常过滤器 when
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| try { } catch (HttpRequestException ex) { if (ex.Message.Contains("404")) { HandleNotFound(); } else { throw; } }
try { } catch (HttpRequestException ex) when (ex.Message.Contains("404")) { HandleNotFound(); } catch (HttpRequestException ex) when (ex.Message.Contains("500")) { HandleServerError(); }
|
when
过滤器还有一个强大用途:调试时记录所有异常而不干扰异常传播:
1 2 3 4 5
| catch (Exception ex) when (Log.Error("异常发生", ex) == false) { }
|
7. using static
1 2 3 4 5 6
| using static System.Math; using static System.Console;
double result = Sqrt(Pow(3, 2) + Pow(4, 2)); WriteLine($"结果:{result}");
|
对于频繁使用
Math、Console、Enumerable
的代码,using static 可以减少很多重复的类名前缀。
总结
C# 6.0 的新特性按实用价值排序:
?. null 条件运算符 |
★★★★★ |
极高 |
$"" 字符串插值 |
★★★★★ |
极高 |
nameof |
★★★★☆ |
高 |
| 表达式体成员 |
★★★★☆ |
高 |
异常过滤器 when |
★★★☆☆ |
中 |
using static |
★★★☆☆ |
中 |
| 字典初始化 |
★★☆☆☆ |
低 |
?. 和 $"" 是立竿见影的,升级到 C# 6.0
第一天就应该开始用。nameof
在重构频繁的项目里非常有价值,能让 IDE 的重命名功能真正安全地工作。