概述

尽管 C# 6.0 尚未完成,但现在这些功能正处于接近完成的关键时刻。自 2014 年 5 月发布文章“C# 6.0 语言预览版”(msdn.microsoft.com/magazine/dn683793.aspx) 以来,下一版本的 Visual Studio 的 CTP3 版本中对 C# 6.0 进行了一些变更和改进(代号为“14”)。

Null 条件运算符

即使是 .NET 开发新手,也可能非常熟悉 NullReferenceException。有一个例外是几乎总是会指出一个 Bug,因为开发人员在调用 (null) 对象的成员之前未进行充分的 null 检查。请看看以下示例:

public static string Truncate(string value, int length)
{
string result = value;
if (value != null) // Skip empty string check for elucidation
{
result = value.Substring(, Math.Min(value.Length, length));
}
return result;
}

如果不进行 null 检查,此方法会引发 NullReferenceException。尽管这很简单,但检查字符串参数是否为 null 的过程却稍微有些繁琐。通常,考虑到比较的频率,该繁琐的方法可能没有必要。C# 6.0 包括一个新的 null 条件运算符,可帮助您更加简便地编写这些检查:

public static string Truncate(string value, int length)
{
return value?.Substring(, Math.Min(value.Length, length));
}
[TestMethod]
public void Truncate_WithNull_ReturnsNull()
{
Assert.AreEqual<string>(null, Truncate(null, ));
}

根据 Truncate_WithNull_ReturnsNull 方法所演示的内容,如果对象的值实际上为 null,则 null 条件运算符将返回 null。这带来了一个问题,即 null 条件运算符在调用链中出现时会是什么情况?如以下示例中所示:

public static string AdjustWidth(string value, int length)
{
return value?.Substring(, Math.Min(value.Length, length)).PadRight(length);
}
[TestMethod]
public void AdjustWidth_GivenInigoMontoya42_ReturnsInigoMontoyaExtended()
{
Assert.AreEqual<int>(, AdjustWidth("Inigo Montoya", ).Length);
}

尽管 Substring 是通过 null 条件运算符进行调用的,并且 null value?.Substring 似乎返回了 null,但语言行为按您的想法进行。这简化了对 PadRight 的调用过程,并立即返回 null,从而避免会导致出现 NullReferenceException 的编程错误。这个概念称为“null 传播”。

Null 条件运算符会根据具体条件进行 null 检查,然后再调用目标方法以及调用链中的所有其他方法。这将可能产生一个令人惊讶的结果,例如,text?.Length.GetType 语句中的结果。

如果 null 条件运算符在调用目标为 null 时返回 null,那么调用会返回值类型的成员时最终会是什么数据类型(假定值类型不能为 null)?例如,从 value?.Length 返回的数据类型不能只是 int。答案当然是:可以为 null 的类型(int?)。实际上,尝试仅将结果分配给 int 将会出现编译错误:

int length = text?.Length; // Compile Error: Cannot implicitly convert type 'int?' to 'int'

Null 条件具有两种语法形式。首先,问号在点运算符前面 (?.)。其次,将问号和索引运算符结合使用。例如,给定一个集合(而非在索引到集合之前显式进行 null 检查),您就可以使用 null 条件运算符执行此操作:

public static IEnumerable<T> GetValueTypeItems<T>(
IList<T> collection, params int[] indexes)
where T : struct
{
foreach (int index in indexes)
{
T? item = collection?[index];
if (item != null) yield return (T)item;
}
}

此示例使用了运算符 ?[…] 的 null 条件索引形式,导致仅在集合不为 null 时才索引到集合。通过 null 条件运算符的此形式,T? item = collection?[index] 语句在行为上相当于:

T? item = (collection != null) ? collection[index] : null.

请注意,null 条件运算符仅可检索项目,不会分配项目。如果给定 null 集合,那么这意味着什么?

请注意针对引用类型使用 ?[…] 时的隐式歧义。由于引用类型可以为 null,因此对于集合是否为 null,或者是否元素本身实际上就是 null 而言,来自 ?[…] 运算符的 null 结果不明确。

Null 条件运算符的一个非常有用的应用程序解决了 C# 自 C# 1.0 以来一直存在的的一个特性,即在调用委托之前检查是否为 null。我们来看一下图 1 中显示的 C# 2.0 代码。

//在调用委托之前检查是否为 Null

class Theremostat
{
event EventHandler<float> OnTemperatureChanged;
private int _Temperature;
public int Temperature
{
get
{
return _Temperature;
}
set
{
// If there are any subscribers, then
// notify them of changes in temperature
EventHandler<float> localOnChanged =
OnTemperatureChanged;
if (localOnChanged != null)
{
_Temperature = value;
// Call subscribers
localOnChanged(this, value);
}
}
}
}

通过使用 null 条件运算符,整个 set 实现过程就可简化为:

OnTemperatureChanged?.Invoke(this, value)

现在,您只需对将 null 条件运算符作为前缀的 Invoke 进行调用,不再需要将委托实例分配给本地变量,从而实现线程安全,甚至是在调用委托之前显式检查值是否为 null。

C# 开发人员都很想知道在最新的四个版本中是否对此内容有所改进。答案是最终进行了改进。仅此一项功能就可以改变调用委托的方式。

另一个 null 条件运算符普及的常见模式是与 coalesce 运算符结合使用。您无需在调用 Length 之前对 linesOfCode 进行 null 检查,而是可以编写项目计数算法,如下所示:

List<string> linesOfCode = ParseSourceCodeFile("Program.cs");
return linesOfCode?.Count ?? ;

在这种情况下,任何空集合(无项目)和 null 集合均标准化为返回相同数量。总之,null 条件运算符将实现以下功能:

  • 如果操作数为 null,则返回 null
  • 如果操作数为 null,则简化调用链中的其他调用
  • 如果目标成员返回一个值类型,则返回可以为 null 的类型 (System.Nullable<T>)。
  • 以线程安全的方式支持委托调用
  • 可用作成员运算符 (?.) 和索引运算符 (?[…])

原文出处:http://msdn.microsoft.com/zh-cn/magazine/dn802602.aspx

待续

C# 6.0 新特性 (一)的更多相关文章

  1. 浅谈Tuple之C#4.0新特性那些事儿你还记得多少?

    来源:微信公众号CodeL 今天给大家分享的内容基于前几天收到的一条留言信息,留言内容是这样的: 看了这位网友的留言相信有不少刚接触开发的童鞋们也会有同样的困惑,除了用新建类作为桥梁之外还有什么好的办 ...

  2. Java基础和JDK5.0新特性

    Java基础 JDK5.0新特性 PS: JDK:Java Development KitsJRE: Java Runtime EvironmentJRE = JVM + ClassLibary JV ...

  3. Visual Studio 2015速递(1)——C#6.0新特性怎么用

    系列文章 Visual Studio 2015速递(1)——C#6.0新特性怎么用 Visual Studio 2015速递(2)——提升效率和质量(VS2015核心竞争力) Visual Studi ...

  4. atitit.Servlet2.5 Servlet 3.0 新特性 jsp2.0 jsp2.1 jsp2.2新特性

    atitit.Servlet2.5 Servlet 3.0 新特性 jsp2.0 jsp2.1 jsp2.2新特性   1.1. Servlet和JSP规范版本对应关系:1 1.2. Servlet2 ...

  5. 背水一战 Windows 10 (1) - C# 6.0 新特性

    [源码下载] 背水一战 Windows 10 (1) - C# 6.0 新特性 作者:webabcd 介绍背水一战 Windows 10 之 C# 6.0 新特性 介绍 C# 6.0 的新特性 示例1 ...

  6. C# 7.0 新特性2: 本地方法

    本文参考Roslyn项目中的Issue:#259. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ...

  7. C# 7.0 新特性1: 基于Tuple的“多”返回值方法

    本文基于Roslyn项目中的Issue:#347 展开讨论. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: ...

  8. C# 7.0 新特性3: 模式匹配

    本文参考Roslyn项目Issue:#206,及Docs:#patterns. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# ...

  9. C# 7.0 新特性4: 返回引用

    本文参考Roslyn项目中的Issue:#118. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ...

  10. C#发展历程以及C#6.0新特性

    一.C#发展历程 下图是自己整理列出了C#每次重要更新的时间及增加的新特性,对于了解C#这些年的发展历程,对C#的认识更加全面,是有帮助的. 二.C#6.0新特性 1.字符串插值 (String In ...

随机推荐

  1. Spring MVC @PathVariable注解

    下面用代码来演示@PathVariable传参方式 @RequestMapping("/user/{id}") public String test(@PathVariable(& ...

  2. ubuntu 16.04 镜像下载

    下载地址: http://mirror.pnl.gov/releases/xenial/ Ubuntu 14.04.5 LTS (Trusty Tahr)http://releases.ubuntu. ...

  3. 「git」mac下git提交github代码

    1.打开终端,输入 cd -/.ssh 这个是检查你的ssh的是否存在的,如果存在,先将已有的ssh备份,或者将新建的ssh生成到另外的目录下(如果第一次配置一般都是不存在的),不存在,你将会看到如下 ...

  4. P4753 River Jumping

    P4753 River Jumping 题目描述 有一条宽度为 NN 的河上,小D位于坐标为 00 的河岸上,他想到达坐标为 NN 的河岸上后再回到坐标为 00 的位置.在到达坐标为 NN 的河岸之前 ...

  5. myeclipse注册机,自己生成注册码

    话不多少,直接上代码 直接可以运行,自己输入key即可 package com.generate.myeclipse.code; import java.io.BufferedReader; impo ...

  6. ArrayList既然继承自AbstractList抽象类,而AbstractList已经实现了List接口,那么ArrayList类为何还要再实现List接口呢?

    https://www.cnblogs.com/bluejavababy/p/4320545.html

  7. Redis-1-Redis的安装

    Redis 什么是Redis? redis是一个开源的.使用C语言编写的.支持网络交互的.可基于内存也可持久化的Key-Value数据库. 安装Redis: windows下如何安装? 官方网址:ht ...

  8. 解决gridview row 左边序列号 显示不完全的技巧

    放在主程序 入口处, public Form1() { InitializeComponent(); gridView1.IndicatorWidth = ; //<宽度值>官方推荐常用是 ...

  9. 使用SPLUNK进行简单Threat Hunting

    通过订阅网上公开的恶意ip库(威胁情报),与SIEM平台中网络流量日志进行匹配,获得安全事件告警. 比如,这里有一个malware urls数据下载的网站,每天更新一次: https://urlhau ...

  10. PowerPC简单了解

    PowerPC相对于ARM优势: Powerpc芯片凭借其出色的性能和高度整合和技术先进特性在网络通信应用,工业控制应用,家用数字化,网络存储领域,军工领域,电力系统控制等都具有非常广泛的应用.由于P ...