这篇文章来自发布在MVP 项目组博客上的文章: http://blogs.msdn.com/b/mvpawardprogram/archive/2012/03/26/c.aspx。 英文版: http://blogs.msdn.com/b/mvpawardprogram/archive/2012/03/26/introduction-of-new-features-in-c-5-0.aspx。
C#如今已经发展到5.0版本,CLR版本为4.5,伴随Visual Studio 2011发布。我总结了一个进化图,以供大家参考。
在C# 5.0中主要增加了Async Programming 以及Caller Information两个特性,以下分别作介绍。
在C# 5.0新增了async修饰符以及await操作符;标记有async的方法被称为异步方法。 异步编程可以给我们带来很大的便利。比如在WinForm编程中,当我们使用HttpWebRequest请求网络资源的时候, 如果使用同步请求,那么如果请求响应时间过长,会导致我们的UI线程堵塞, 从直观上的感受是窗体无响应或者无法进行UI交互操作。
private void btnTest_Click(object sender, EventArgs e) { var request = WebRequest.Create(txtUrl.Text.Trim()); var content=new MemoryStream(); using (var response = request.GetResponse()) { using (var responseStream = response.GetResponseStream()) { responseStream.CopyTo(content); } } txtResult.Text = content.Length.ToString(); }
当点击Test按钮后,在txtResult显示结果之前,我们将不能对窗体进行任何操作。
在没有async之前,我们一般也可以使用BeginGetResponse方法进行异步操作,如MSDN文档上的示例所示, 我们需要编写大量的代码去实现异步的效果: http://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.begingetresponse(v=vs.80).aspx。
下面我们将对以上的窗体进行改造,使用新增的异步编程特性,代码如下:
private async void btnTest_Click(object sender, EventArgs e) { var request = WebRequest.Create(txtUrl.Text.Trim()); var content = new MemoryStream(); Task<WebResponse> responseTask = request.GetResponseAsync(); using (var response = await responseTask) {
using (var responseStream = response.GetResponseStream()) { Task copyTask = responseStream.CopyToAsync(content); //await operator to supends the excution of the method until the task is completed. In the meantime, the control is returned the UI thread. await copyTask; } } txtResult.Text = content.Length.ToString(); }
通过await,可以使我们在语义上让我们理解为reponse为异步执行后的结果,而编译器会负责所有的代码生成, 我们无需再去操作复杂的Callback。这样为我们异步编程节省很多的时间。从直观上,当我们使用以上代码后, 窗体在点击Test按钮后,我们还可以进行���互操作。
从字面上,我们可以理解为在被调用者方法中可以获得调用者的信息,这对我们开发跟踪、 调试以及诊断工具的时候特别有用。而在之前,我们可能需要在调用者方法中自己进行相关操作, 比如插入日志信息记录哪个方法执行了等。我印象很深的是,我参与的一个项目根据要求使用微软的企业库, 在调用每个Data Access的方法前都要插入调用者的信息到日志中,以便跟踪调查等; 这样导致每个调用者方法都会有相同的调用记录日志的方法的代码。有了Caller Information, 我们可以在被调用者方法中获得以下几个信息:
CallerFilePathAttribute 调用者方法所在的源文件地址
CallerLineNumberAttribute 方法被调用的行号
CallerMemberNameAttribute 调用方法的名称
下面我将举例说明。
在以前我们可能会用到如下的方法:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;
namespace ConsoleApplicationTest { class Program { static void Main(string[] args) { InsertLog("Main"); MethodB(); Console.ReadLine(); }
static void MethodA() { InsertLog("MethodA"); MethodB(); }
static void MethodB() { }
static void InsertLog(string methodName) { Console.WriteLine("{0} called method B at {1}", methodName, DateTime.Now); } } }
在Main和MethodA中都调用了InsertLog方法。有了新特性的支持,我们可以修改代码为:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks;
namespace ConsoleApplicationTest { class Program { static void Main(string[] args) { //InsertLog("Main"); MethodB(); Console.ReadLine(); }
static void MethodA() { //InsertLog("MethodA"); MethodB(); }
static void MethodB( [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) { InsertLog(memberName); }
C# 5.0新增的特性可以提高我们编程的效率同时减少代码量,VS11 IDE方面也新增加了很多新的功能, 大家可以慢慢去体会。
Thanks for your contribution. Great article!