自动属性初始化表达式
有过正确实现结构经验的所有 .NET 开发人员无疑都为一个问题所困扰:需要使用多少语法才能使类型固定不变(为 .NET 标准建议的类型)。此问题实际上是只读属性存在的问题:
- 定义为只读的支持字段
- 构造函数内支持字段的初始化
- 属性的显式实现(而非使用自动属性)
- 返回支持字段的显式 getter 实现
所有这一切仅仅是为了“正确地”实现固定不变的属性。之后,此情况还会针对类型的所有属性重复发生。因此,正确操作需要比不堪一击的方法付出明显更多的努力。发布了自动属性初始化表达式(CTP3 还包括对初始化表达式的支持)这个新功能后,C# 6.0 就可派上用场了。自动属性初始化表达式允许直接在属性的声明内分配属性。对于只读属性,它负责确保属性固定不变所需的所有繁琐程序。例如,请看本示例中的 FingerPrint 类:
public class FingerPrint { public DateTime TimeStamp { get; } = DateTime.UtcNow; public string User { get; } = System.Security.Principal.WindowsPrincipal.Current.Identity.Name; public string Process { get; } = System.Diagnostics.Process.GetCurrentProcess().ProcessName; }
如代码所示,属性初始化表达式允许向属性分配一个初始值作为属性声明的一部分。属性可以是只读的(只包含 getter),也可以是读/写(包含 setter 和 getter)的。如果是只读的,则基础支持字段将通过只读修饰符自动声明。这就确保了在初始化之后会固定不变。
初始化表达式可以是任意表达式。例如,通过使用条件运算符,您可以设置默认初始化值:
public string Config { get; } = string.IsNullOrWhiteSpace( string connectionString = (string)Properties.Settings.Default.Context?["connectionString"])? connectionString : "<none>";
本示例中,请注意之前的文章中所讨论的如何使用声明表达式(请参阅 itl.tc/?p=4040)。如果您需要的不只是表达式,可以将初始化重构到静态方法中,然后对其进行调用。
Nameof 表达式
CTP3 版本中介绍的另一个新增功能是支持 nameof 表达式。您将多次需要在代码中使用“魔幻字符串”。此类“魔幻字符串”是映射到您代码中的程序元素的普通 C# 字符串。例如,引发 ArgumentNullException 时,使用一个字符串表示无效对应参数的名称。遗憾的是,这些魔幻字符串未经过编译时验证,任意程序元素更改(例如,重命名参数)都不会自动更新魔幻字符串,从而导致不一致,而编译器根本不会发现此问题。
在其他情况下,例如引发 OnPropertyChanged 事件时,可以通过提取名称的树表达式技术避免出现魔幻字符串。鉴于操作简单(只识别程序元素名称),所以这或许有点让人头疼。无论哪种情况,解决方案都不太理想。
若要解决这一特性,C# 6.0 提供了对“程序元素”名称的访问权限,无论是类名称、方法名称、参数名称还是特定属性名称(可能是对于使用反射的情况)。例如,图 2 中的代码使用 nameof 表达式提取参数的名称。
//使用 Nameof 表达式提取参数名称 void ThrowArgumentNullExceptionUsingNameOf(string param1) { throw new ArgumentNullException(nameof(param1)); } [TestMethod] public void NameOf_UsingNameofExpressionInArgumentNullException() { try { ThrowArgumentNullExceptionUsingNameOf("data"); Assert.Fail("This code should not be reached"); } catch (ArgumentNullException exception) { Assert.AreEqual<string>("param1", exception.ParamName); }
正如测试方法所演示的,ArgumentNullException 的 ParamName 属性具有 param1 值,这是使用方法中的 nameof(param1) 表达式的值集。Nameof 表达式不仅仅用于参数,您还可以使用它来检索所有编程元素。
//检索其他编程元素 namespace CSharp6.Tests { [TestClass] public class NameofTests { [TestMethod] public void Nameof_ExtractsName() { Assert.AreEqual<string>("NameofTests", nameof(NameofTests)); Assert.AreEqual<string>("TestMethodAttribute", nameof(TestMethodAttribute)); Assert.AreEqual<string>("TestMethodAttribute", nameof( Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute)); Assert.AreEqual<string>("Nameof_ExtractsName", string.Format("{0}", nameof(Nameof_ExtractsName))); Assert.AreEqual<string>("Nameof_ExtractsName", string.Format("{0}", nameof( CSharp6.Tests.NameofTests.Nameof_ExtractsName))); } } }
Nameof 表达式仅检索最终的标识符,即使您使用更多的显式包含点的名称也是如此。此外,对于属性而言,未隐含“Attribute”后缀。相反,编译需要它。它非常适合用于清理混乱代码。
原文出处:http://msdn.microsoft.com/zh-cn/magazine/dn802602.aspx