之所以在這裡分享這個對應關係,是因為在C#基礎知識系列的文章釋出之後,有些初學者對.NET版本和C#語言特性之間的對應關係有點不清楚,有時候會弄混淆了。

並且通過這個對應關係,也可以幫助大家對C#和.NET 類庫有個全面的把控,可以幫助大家理清楚C#和.NET 類庫中各個知識點,使他們可以對號入坐。具體他們的之間對應關係見下表:

版本 .NET Framework版本 Visual Studio版本 釋出日期 特性
C# 1.0 .NET Framework 1.0 Visual Studio .NET 2002 2002.1 委託
事件
C# 1.1 .NET Framework 1.1 Visual Studio .NET 2003 2003.4 APM
C# 2.0 .NET Framework 2.0 Visual Studio 2005(開始命名為Visual Studio) 2005.11 泛型
匿名方法
迭代器
可空型別
C# 3.0 .NET Framework 3.0 Visual Studio 2008 2007.11
.NET Framework 3.5
匿名型別
擴充套件方法
查詢表示式
Lambda表示式
表示式樹
Linq
C# 4.0 .NET Framework 4.0 Visual Studio 2010 2010.4 動態繫結
互操作性
C# 5.0 .NET Framework 4.5 Visual Studio 2012 2012.8 非同步和等待(async和await)
呼叫方資訊(Caller Information)

這裡寫圖片描述

C# 5.0中新增特性

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++中,我們可以通過定義一個巨集,然後再巨集中通過FILELINE來獲取當前程式碼的位置,但C#並不支援巨集,往往只能通過StackTrace來實現這一功能,但StackTrace卻有不是很靠譜,常常獲取不了我們所要的結果。

針對這個問題,在.Net 4.5中引入了三個Attribute:CallerMemberNameCallerFilePathCallerLineNumber。在編譯器的配合下,分別可以獲取到呼叫函式(準確講應該是成員)名稱,呼叫檔案及呼叫行號。上面的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”。
特性建構函式 特性所應用的成員的名稱

C# 5.0五大新特性

第一:繫結運算子,:=:

這個只是簡化了資料繫結,跟ASP.NET MVC3不斷改進一樣,其實不是什麼亮點改進。

    comboBox1.Text :=: textBox1.Text; //將文字框的內容繫結到下拉框。  

第二:帶引數的泛型建構函式:

這個的加入給一些設計增加了強大功能,泛型早在C#2.0加入後就有著強大的應用,一般稍微設計比較好的框架,都會用到泛型,c#5.0加入帶引數泛型建構函式,則在原有基礎上對C#泛型完善了很多。:)

    public class T MyClass : T: class, new()  
    public class T MyClass : T:class, new(int)  

第三:支援null型別運算:

此功能,個人覺得並非什麼大的亮點,但至少對null型別,特別是有資料計算的這種null型別的支援,寫程式碼還是方便不少。

注意對於Nullable Types,在C#2.0就加入進來了,但是不支援計算,比如:

int? x = null;

int? y = x + 40;
那麼y值是多少?不支援計算,得到的是null,想必大家知道為什麼結果是null了吧?但C#5.0可以,40加一個null的整數,我們要的結果是40,不過份吧?

    int x? = null;  
    int y? = x + 40;  
    Myobject obj = null;  
    Myotherobj obj2 = obj.MyProperty ??? new Myotherobj();  

第四:case支援表示式:

這個是一個我很早就想如果能這樣就好了,沒想到在C#5.0裡就加入此功能,以前case裡只能寫一個具體的常量,而現在可以加表示式了,靈活多了。

    switch(myobj){  
    llorEmpty(myotherobj):  
    //邏輯程式碼  
    case myotherobj.Trim().Lower:  
    //邏輯程式碼  
    }  

第五:擴充套件屬性。

我們在C#3.0裡有擴充套件方法,那麼在C#5.0裡將會加入擴充套件屬性的感念,對照擴充套件方法,不難理解擴充套件屬性的概念了。以下為擴充套件屬性的定義舉例:

[Associate(string)]

public static int Zivsoft_ExtensionProperty { get;set;}

C#5.0 遠遠不只是上面描述的5點新功能,它如同C#4.0加入dynamic概念一樣,會加入非同步處理概念,這個不是幾行程式碼就能表達,而是將在設計,架構上,又會掀起一次飛躍……

為了大家搶先看,就給一段C#5.0一段簡單的非同步操作的程式碼例子,注意(C#5.0兩個新加的關鍵字async, await):

    Task<Movie> GetMovieAsync(string title);  

    Task PlayMovieAsync(Movie movie);  

    async void GetAndPlayMoviesAsync(string[] titles)  
    {  
        foreach (var title in titles)  
         {  
             var movie = await GetMovieAsync(title);  

             await PlayMovieAsync(movie);  
         }  
    }