C# 7编程模式与实践(7)

正由于此,所有的异步方法默认应返回一个Task或是Task<TResult>,除非性能分析表明使用ValueTask<TResult>要优于使用Task<TResult>。并不存在非泛型的ValueTask<TResult>,因为当返回Task的方法异步成功完成时,可使用Task.CompletedTask属性交回成功完成的单例(Singleton)。

这段话相当长,我们概括为下面的指导原则。

ValueTask<T>的指导原则

当对性能敏感的代码通常同步返回结果时,考虑使用ValueTask<T>。

当存在内存压力问题并且不能存储Task时,考虑使用ValueTask<T>

X 避免在公开API中暴露ValueTask<T>,除非存在显著的性能影响。

X 不要在调用Task.WhenAll或WhenAny方法时使用ValueTask<T>。

表达式体成员(Expression Bodied Members)

表达式体成员使得开发人员可以在声明简单函数时不使用大括号。对于传统的四行函数,通常能缩减为一行。例如:

public override string ToString() { return FirstName + " " + LastName; } public override string ToString() => FirstName + " " + LastName;

需格外小心的是,不要过度使用该特性。例如,如果要实现在FirstName为空时不会生成开头处的空格,可以这样编写代码:

public override string ToString() => !string.IsNullOrEmpty(FirstName) ? FirstName + " " + LastName : LastName;

但是,还需要检查是否存在LastName同时缺失的情况:

public override string ToString() => !string.IsNullOrEmpty(FirstName) ? FirstName + " " + LastName : (!string.IsNullOrEmpty(LastName) ? LastName : "No Name");

正如在本例中所看到的,使用该特性后,很快就会失去对代码的控制。因此,虽然将多个分支条件串联在一起或是使用空值合并(null-coalescing)操作符可以实现不少功能,但是应尽量克制使用这样的设计。

表达式体属性(Expression Bodied Properties)

表达式体属性是在C# 6中新提出的特性,对于使用Get/Set方法处理属性通知等事情的MVVM模型,该特性非常有用。

下面给出一个C# 6代码:

public string FirstName { get { return Get<string>(); } set { Set(value); } }

在C# 7中实现为:

public string FirstName { get => Get<string>(); set => Set(value); }

虽然代码的行数并未减少,但是不少代码行中的噪音(line-noise)消失了。对于属性这样的规模很小但是重复出现的实体,即使减少一个比特都会产生聚沙成塔的效果。

如果想了解Get/Set工作方式的详细信息,可参见“C#和VB.NET获得Windows Runtime支持和异步方法”一文中的“CallerMemberName”部分。

表达式体构造函数(Expression Bodied Constructors)

表达式体构造函数同样是C# 7新引入的特性。下面给出一个例子:

class Person { public Person(string name) => Name = name; public string Name { get; } }

这里的用法非常受限。代码只在没有参数或是一个参数时工作。一旦添加了另一个需为字段或属性的参数,必须切换回传统的构造函数。该用法也不能初始化其它字段,或是钩到事件处理器(但是可以做参数验证,参见下文“Throw表达式”一章内容)。

因此,我们的建议是忽略该特性。它只是让单参数的构造函数看上去不同于一般的构造函数而已,对减少代码量的贡献很小。

表达式体析构函数(Expression Bodied Destructors)

为使C#更为一致,C# 7允许表达式体成员是一个析构函数,正如表达式体成员可以是一个方法或一个构造函数。

为避免有人忘记了析构的概念,我们对此稍作解释。在C#中,析构函数事实上是覆写了System.Object中Finalize方法,虽然C#并不用以这一方式表述。例如:

~UnmanagedResource() { ReleaseResources(); }

该语法存在一个问题,就是构函数看上去类似于一个构造函数,导致易被忽视。另一个问题是,它模仿了C++中的析构语法,但是在C++中析构语法具有完全不同的语义。该语法已经这样地使用很久了,所以让我们继续使用这一语法:

~UnmanagedResource() => ReleaseResources();

该代码只有一行,易于被忽视,它实现了将对象加入到终结器队列的周期中。这并非一个无关紧要的属性或是一个ToString方法,而是一个值得关注的重要操作。我们再一次建议不要使用该特性。

表达式体成员的指导原则

对简单属性不要使用表达式体成员。

对于调用同一函数中其它重载的方法,一定要使用表达式体成员。

考虑对非关键函数使用表达式体成员。

X 不要在表达式体成员中使用多于一个条件(a ? b : c),或是使用空值合并(x ?? y)。

X 不要对构造函数和析构函数使用表达式体成员。

throw表达式

编程语言通常可将粗略地分成两类:

凡事皆表达式;

语句、声明和表达式分别是独立的概念。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/82947a4b5f7533883a344c52de3e78ac.html