第6条:不要用表示符号名称的硬字符串来调用API
需要编写分布式程序的场合越来越多了,这些程序要在不同的系统之间移动大量的数据,使得开发者必须采用各种各样的程序库来应对此类需求。这些库可能会通过数据的名称与字符串标识符来运作,这在跨平台与跨语言的环境中确实是个很方便的做法。然而这种办法也是有代价的,因为类型安全无法得到保证,而且无法获得相关工具的支持,静态类型的语言所带来的很多好处也都发挥不出来。
C#语言的设计团队意识到了这个问题,并在6.0版本里面添加了nameof()表达式。这个关键字可以根据变量来获取包含其名称的字符串,使得开发者不用把变量名直接写成字面量。实现INotifyPropertyChanged接口时,经常要用到nameof:
用nameof运算符来写代码的好处是,如果属性名变了,那么用来构造Property-ChangedEventArgs对象的参数也会随之变化。这是nameof()的基本用法。
nameof()会根据符号求出表示该符号名称的字符串,这个符号可以指类型、变量、接口及命名空间。符号既可以写成非限定的形式,也可以写成完全限定的形式。针对泛型类来使用nameof时,会受到一些限制,因为nameof只支持封闭的泛型类,也就是说,开发者必须把所有的类型参数全都指定出来。
nameof运算符需要应对各种各样的符号,然而它在面对这些符号时也应该表现出协调一致的行为,为此,该操作符总是返回局部名称。即使变量是用完全限定的方式传给nameof的,它也依然会返回局部名称。例如把System.Int.MaxValue传给它,会得到MaxValue。
这种基本的用法许多开发者是明白的,而且在调用那些以变量名称为参数的API时,都能够正确地运用nameof来获取该名称。但还有一些地方也可以用nameof来写,只是很多人没有意识到这一点,而是沿用了固有的写法。
某些异常类型的构造函数可以接受string参数,使得开发者能够把该异常所涉及的变量名传给这个参数,从而构造更为明确的异常信息。调用这样的构造函数时,不应该把变量的名字写成硬字符串,而应该使用nameof来获取其名称,以便使代码在变量名改变之后,依然能够正常运作:
用了这种写法之后,静态分析工具就可以判断出开发者在调用ArgumentNull-Exception的构造函数时有没有把相关变量的名称放在正确的位置上。由于构造函数的两个参数都是string,因此容易写错地方。
在指定attribute的参数(无论是位置参数还是命名参数)时,可能会用到字符串,这种字符串可以通过nameof运算符来构造。在定义MVC应用程序或Web API应用程序的route时,也可以考虑用nameof将某个命名空间的名称设置成route的名称。
使用nameof运算符的好处是,如果符号改名了,那么用nameof来获取符号名称的地方也会获取到修改之后的新名字。各种静态分析工具可以借此找到参数名称与参数位置方面的错误,这些工具包括运行在编辑器或IDE(集成开发环境)中的诊断工具、构建与持续集成(Continuous Integration,CI)工具以及重构工具等。这种写法可以保留较多的符号信息,使得自动化工具能够多发现并多修复一些错误,从而令开发者可以专心解决那些更为困难的问题。如果不这样做,那么有些错误就只能通过自动化测试及人工检查才能寻找出来。