微软提议在 C# 7 中为引用增加选项类型

阅读数:1744 2015 年 9 月 8 日

话题:.NET语言 & 开发架构

微软 C# 语言的项目经理 Mads Torgersen,最近提议在 C# 7 中引入可为选项类型(option types)的引用类型。加入选项类型能让 C# 语言变得更安全,正如 Torgersen 所说,在 C# 里,因为任一引用类型都能引用一个空值,从而导致了空引用异常的泛滥成灾,而使用选项类型能减少空引用异常的产生。

C# 已经对nullable 值类型提供了支持,即对基于 struct 的已提供了支持,但不支持基于 class 的引用类型。对一门现存的已然成熟的编程语言事后加入选项类型,这其中存在着巨大的复杂性,出于这一原因,Torgersen 不打算建立一套“滴水不漏的”机制,而是在检测到代码可能存在解引用空值的时候发出警告信息。

新的提议方案对一个现有的引用类型 T 有如下约定:

  • T 用来表示一个非 nullable 类型;
  • T? 用来表示一个 nullable 引用类型;
  • 在以下情况下,编译器将发出告警信息:
    • 一个 nullable 的T? 类型被解引用或者转化为一个非nullable 类型;
    • 将 null/default(T) 赋值给非 nullable T 类型变量;
    • 流分析检测到一个 nullable 引用很可能不再是空值;
    • 构造函数在返回前未对非 nullable 引用进行赋值;
    • 构造函数使用尚未赋值的非 nullable 引用。

另一方面,当一个非 nullable 数组在进行初始化时,无法确保其中为 null 的数组成员不被保留下来。

使用空条件操作符null-conditional operator)?.,可参考介绍 C# 6,将产生以下效果:

string s;
string? ns = SomeStringMaybe();

s = ns;                     // emits warning
if (ns != null) { s = ns; } // ok

WriteLine(ns.Length);       // emits warning
WriteLine(ns?.Length);      // ok

尽管使用这种新设计出的方法不用改动任何的现有代码,但该方法仍可能对编译器的行为产生潜在的影响,因为在当前的 C# 里,T 实际上用来表示一个 nullable 类型。因此,Torgersen 说,需要另外再新增一套机制来关闭告警信息,用于确保在跨 C# 版本和程序集下的编译兼容性。

需要重点声明的是,当所赋的值为 null 时,若编译器只产生告警信息,那么 C# 7 仍将会允许一个非 nullable 类型 T 包含 null 类型,这主要是出于对程序集兼容性的考虑。这也使得 C# 7 表现得与其他编程语言大相径庭,比如Swift option typesHaskell Maybe,在 Swift 和 Haskell 中,选项类型可被视为对基本类型的一种封装(实际上,在 Swift 里选项类型就是泛型类型,在 Haskell 里就是 monad)。

从通告的评论来看,人们对微软的提议反应不一。有些人担心在重新定义了类型 T 为非 nullable 之后,现存的 C# 代码会产生大量的对他们来说毫无意义的告警信息。还有些人认为应该用 T! 或者其他不同于基本类型 T 的表示方式来表征非 nullable 类型,基本类型 T 继续用来表示 nullable 类型。微软的提议方法没有让任何已有的代码受益,应对其进行重构以使其有助于代码的 non-nullability。另外有部分人支持该提议,并表示自己更喜欢这种“严谨的”模式,在这种模式下,空值经由 option<T> 或其它等效结构被有效地封装了起来。

查看英文原文A Proposal to Add Option Types for References to C# 7