3.4 C# 5.0的新特性
C#中提出的每个新特性都建立在原来特性的基础上,并且是对原来特性的一个改进,做这么多的改进主要是为了方便开发人员更好地使用C#来编写程序,可写更少的代码,把一些额外的工作交给编译器去做,C# 5.0同样也是如此。
3.4.1 全新的异步编程模型
对于同步的代码,大家肯定都不陌生,因为平常写的代码大部分都是同步的,然而同步代码却存在一个很严重的问题。例如,向一个Web服务器发出一个请求时,如果发出请求的代码是同步实现的话,这时候应用程序就会处于等待状态,直到收回一个响应信息为止,然而在这个等待状态下,用户不能操作任何的UI界面也没有任何的消息,如果试图去操作界面时,就会看到“应用程序为响应”的信息(在应用程序的窗口旁),相信大家在平常使用桌面软件或者访问Web的时候,肯定都遇到过类似的情况。对于这个,大家肯定会觉得不舒服。引起这个原因正是因为代码的实现是同步实现的,所以在没有得到一个响应消息之前,界面就成了一个“卡死(阻塞)”状态了,这对用户来说肯定是不友好的,因为如果要从服务器上下载一个很大的文件时,甚至不能对窗体进行关闭操作。
为了解决类似的问题,.NET Framework很早就提供了对异步编程的支持,但是其代码的编写过程非常繁琐。现在,.NET 4.5中推出了新的方式来解决同步代码的问题,它们分别为基于事件的异步模式,基于任务的异步模式和提供async和await关键字来支持异步编程支持,使用这两个关键字,可以使用.NET Framework或Windows Runtime的资源创建一个异步方法,如同创建一个同步的方法一样容易。
全新的异步编程模型使用async和await关键字来编写异步方法。async用来标识方法、lambda表达式或者匿名方法是异步的;await用来标识异步方法应该在此处挂起执行,直到等待的任务完成,于此同时,控制权会移交给异步方法的调用方。
异步方法的参数不能使用ref参数和out参数,但是在异步方法内部可以调用含有这些参数的方法。
以一个标准的逻辑为例,下载一个远程URI,并将内容输出在界面上,假设我们已经有了显示内容的方法,代码如下:
但是用同步的方式会造成线程的阻塞,所以不得不使用下面的异步代码:
上面的代码使用了异步方法,但无可避免的把一段逻辑拆成两段。如果当更多的异步操作交叉在一起的时候,无论是代码的组织还是逻辑的梳理都会变得更加麻烦。
正因为如此,C# 5.0从语法上对此进行了改进,当使用async和await两个关键字时,代码会变成如下所示:
上面的这段代码看上去就是一段典型的同步逻辑,唯一不同地就是在方法声明中加入了async关键字,在DownloadStringTaskAsync方法的调用时加入了await关键字,运行时就变成了异步了。ShowUriContent方法会在调用DownloadStringTaskAsync后退出,而下载过程会异步进行,当下载完成后,再进入Display方法的执行,期间不会阻塞线程,不会造成UI无响应的情况。
使一个异步方法,要注意如下一些要点:
(1)async关键字必须加在函数声明处,如果不加async关键字,函数内部不能使用await关键字。
(2)异步方法的名称以Async后缀,必须按照规定关闭。
(3)await关键字只能用来等待一个Task、Task<TResult>或者void进行异步执行返回:
● Task<TResult>,当方法有返回值时,TResult即返回值的类型。
● Task,如果方法没有返回语句或具有返回语句但不操作时,Task即返回值的类型。
● void,主要用于事件处理程序(不能被等待,无法捕获异常)。
(4)方法通常包括至少一个await的表达式,这意味着该方法在遇到await时不能继续执行,直到等待异步操作完成。在此期间,该方法将被暂停,并且控制权回到该方法的调用者。
3.4.2 调用方信息
在日志组件中,可能需要记录方法调用信息,C# 5.0提供了支持这一功能的方法。使用调用方信息属性可以获取关于调用方的信息要记录的方法,调用信息包括方法成员名称、源文件路径和行号这些信息,用于跟踪、调试和创建诊断工具非常有用。
为了获取这些信息,只需要使用System.Runtime.CompilerServices命名空间下的三个非常有用的编译器特性。表3-12列出了System.Runtime.CompilerServices命名空间中定义的调用方信息属性。
表3-12 调用方信息属性
在使用调用方信息的属性时,要注意以下几个方面:
(1)必须为每个可选参数指定一个显示默认值。
(2)不能将调用方信息属性应用于未指定为选项的参数。
(3)调用方信息属性不会使用一个参数选项。相反,当参数省略时,它们影响传递的默认值。
可以使用CallerMemberName属性来避免指定成员名称作为String参数传递到调用的方法。通过使用这种方法,可以避免重命名重构而不更改String值的问题。这个特性在进行以下一些任务时特别有用:
● 使用跟踪和诊断实例。
● 在绑定数据时,实现INotifyPropertyChanged接口。此接口允许对象的属性通知一个绑定控件的属性已更改,所以该控件可显示最新信息。但CallerMemberName属性必须指定属性的名称为文本类型。
另外,在构造函数、析构函数、属性等特殊的地方调用CallerMemberName属性所标记的函数时,获取的值有所不同,其取值如表3-13所示。
表3-13 返回的值
【实例3-14】调用方信息的使用
本例演示如何使用调用方信息属性,每次调用TraceMessage方法,信息将替换为可选参数的调用方。用Visual Studio 2012编译调试,就能看见文件、行号、调用者方法名称,具体实现步骤如下:
01 启动Visual Studio 2012,创建一个名为“实例10-14”的控制台应用程序。
02 在解决方案资源管理器中单击程序目录下的Program.cs文件,在该文件中编写如下逻辑代码:
上面代码中第3行和第4行使用using关键字引入相关的命名空间。第7行~第15行定义了一个静态的方法TraceMessage,其中第8行~第10行在该方法参数列表的后三个命名参数中使用了三个调用方信息属性,并赋了默认值,第11行~第14行调用Trace类的WriteLine方法将调用方信息写入跟踪侦听器。第16行定义一个Main函数,第17行在该函数中调用上面定义的TraceMessage。
03 按F5快捷键启动调试,编译后在输出窗口中显示如图3-20所示的调用方的信息。
图3-20 显示调用方信息
共有条评论 网友评论