C# 5.0隨着VisualStudio 2012一起正式發布了,讓我們來看看C#5.0中增加了哪些功能。
1. 異步編程
在.Net 4.5中,通過async和await兩個關鍵字,引入了一種新的基於任務的異步編程模型(TAP)。在這種方式下,可以通過類似同步方式編寫異步代碼,極大簡化了異步編程模型。如下式一個簡單的實例:
static async void DownloadStringAsync2(Uri uri)
{
var webClient = new WebClient();
var result = await webClient.DownloadStringTaskAsync(uri);
Console.WriteLine(result);
}
而之前的方式是這樣的:
static void DownloadStringAsync(Uri uri)
{
var webClient = new WebClient();
webClient.DownloadStringCompleted += (s, e) =>
{
Console.WriteLine(e.Result);
};
webClient.DownloadStringAsync(uri);
}
也許前面這個例子不足以體現async和await帶來的優越性,下面這個例子就明顯多了:
public void CopyToAsyncTheHardWay(Stream source, Stream destination)
{
byte[] buffer = new byte[0x1000];
Action<IAsyncResult> readWriteLoop = null;
readWriteLoop = iar =>
{
for (bool isRead = (iar == null); ; isRead = !isRead)
{
switch (isRead)
{
case true:
iar = source.BeginRead(buffer, 0, buffer.Length,
readResult =>
{
if (readResult.CompletedSynchronously) return;
readWriteLoop(readResult);
}, null);
if (!iar.CompletedSynchronously) return;
break;
case false:
int numRead = source.EndRead(iar);
if (numRead == 0)
{
return;
}
iar = destination.BeginWrite(buffer, 0, numRead,
writeResult =>
{
if (writeResult.CompletedSynchronously) return;
destination.EndWrite(writeResult);
readWriteLoop(null);
}, null);
if (!iar.CompletedSynchronously) return;
destination.EndWrite(iar);
break;
}
}
};
readWriteLoop(null);
}
public async Task CopyToAsync(Stream source, Stream destination)
{
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await source.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
await destination.WriteAsync(buffer, 0, numRead);
}
}
關於基於任務的異步編程模型需要介紹的地方還比較多,不是一兩句能說完的,有空的話后面再專門寫篇文章來詳細介紹下。另外也可參看微軟的官方網站:Visual Studio Asynchronous Programming,其官方文檔Task-Based Asynchronous Pattern Overview介紹的非常詳細, VisualStudio中自帶的CSharp Language Specification中也有一些說明。
2. 調用方信息
很多時候,我們需要在運行過程中記錄一些調測的日志信息,如下所示:
public void DoProcessing()
{
TraceMessage("Something happened.");
}
為了調測方便,除了事件信息外,我們往往還需要知道發生該事件的代碼位置以及調用棧信息。在C++中,我們可以通過定義一個宏,然后再宏中通過__FILE__和__LINE__來獲取當前代碼的位置,但C#並不支持宏,往往只能通過StackTrace來實現這一功能,但StackTrace卻有不是很靠譜,常常獲取不了我們所要的結果。
針對這個問題,在.Net 4.5中引入了三個Attribute:CallerMemberName、CallerFilePath和CallerLineNumber。在編譯器的配合下,分別可以獲取到調用函數(准確講應該是成員)名稱,調用文件及調用行號。上面的TraceMessage函數可以實現如下:
public void TraceMessage(string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
{
Trace.WriteLine("message: " + message);
Trace.WriteLine("member name: " + memberName);
Trace.WriteLine("source file path: " + sourceFilePath);
Trace.WriteLine("source line number: " + sourceLineNumber);
}
另外,在構造函數,析構函數、屬性等特殊的地方調用CallerMemberName屬性所標記的函數時,獲取的值有所不同,其取值如下表所示:
調用的地方 |
CallerMemberName獲取的結果 |
方法、屬性或事件 |
方法,屬性或事件的名稱 |
構造函數 |
字符串 ".ctor" |
靜態構造函數 |
字符串 ".cctor" |
析構函數 |
該字符串 "Finalize" |
用戶定義的運算符或轉換 |
生成的名稱成員,例如, "op_Addition"。 |
特性構造函數 |
特性所應用的成員的名稱 |
例如,對於在屬性中調用CallerMemberName所標記的函數即可獲取屬性名稱,通過這種方式可以簡化 INotifyPropertyChanged 接口的實現。關於調用方信息更詳細的資料,請參看MSDN:http://msdn.microsoft.com/zh-cn/library/hh534540.aspx。