1. 程式人生 > >[你必須知道的非同步程式設計]C# 5.0 新特性——Async和Await使非同步程式設計更簡單

[你必須知道的非同步程式設計]C# 5.0 新特性——Async和Await使非同步程式設計更簡單

本專題概要:

引言

同步程式碼存在的問題

傳統的非同步程式設計改善程式的響應

C# 5.0 提供的async和await使非同步程式設計更簡單

 async和await關鍵字剖析

小結

一、引言

 在之前的C#基礎知識系列文章中只介紹了從C#1.0到C#4.0中主要的特性,然而.NET 4.5 的推出,對於C#又有了新特性的增加——就是C#5.0中async和await兩個關鍵字,這兩個關鍵字簡化了非同步程式設計,之所以簡化了,還是因為編譯器給我們做了更多的工作,下面就具體看看編譯器到底在背後幫我們做了哪些複雜的工作的。

二、同步程式碼存在的問題

 對於同步的程式碼,大家肯定都不陌生,因為我們平常寫的程式碼大部分都是同步的,然而同步程式碼卻存在一個很嚴重的問題,例如我們向一個Web伺服器發出一個請求時,如果我們發出請求的程式碼是同步實現的話,這時候我們的應用程式就會處於等待狀態,直到收回一個響應資訊為止,然而在這個等待的狀態,對於使用者不能操作任何的UI介面以及也沒有任何的訊息,如果我們試圖去操作介面時,此時我們就會看到"應用程式為響應"的資訊(在應用程式的視窗旁),相信大家在平常使用桌面軟體或者訪問web的時候,肯定都遇到過這樣類似的情況的,對於這個,大家肯定會覺得看上去非常不舒服。引起這個原因正是因為程式碼的實現是同步實現的,所以在沒有得到一個響應訊息之前,介面就成了一個"卡死"狀態了,所以這對於使用者來說肯定是不可接受的,因為如果我要從伺服器上下載一個很大的檔案時,此時我們甚至不能對窗體進行關閉的操作的。為了具體說明同步程式碼存在的問題(造成介面開始),下面通過一個程式讓大家更形象地看下問題所在:

// 單擊事件
        private void btnClick_Click(object sender, EventArgs e)
        {
            this.btnClick.Enabled = false;

            long length = AccessWeb();
            this.btnClick.Enabled = true;
            // 這裡可以做一些不依賴回覆的操作
            OtherWork();

            this.richTextBox1.Text += String.Format("\n 回覆的位元組長度為:  {0}.\r\n", length);
            txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString();
        }

        private  long AccessWeb()
        {
            MemoryStream content = new MemoryStream();

            // 對MSDN發起一個Web請求
            HttpWebRequest webRequest = WebRequest.Create("http://msdn.microsoft.com/zh-cn/") as HttpWebRequest;
            if (webRequest != null)
            {
                // 返回回覆結果
                using (WebResponse response = webRequest.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        responseStream.CopyTo(content);
                    }
                }
            }

            txbAsynMethodID.Text = Thread.CurrentThread.ManagedThreadId.ToString();
            return content.Length;
        }

執行程式後,當我們點選窗體的 "點選我"按鈕之後,在得到伺服器響應之前,我們不能對窗體進行任何的操作,包括移動窗體,關閉窗體等,具體執行結果如下:

三、傳統的非同步程式設計來改善程式的響應

 上面部分我們已經看到同步方法所帶來的實際問題了,為了解決類似的問題,.NET Framework很早就提供了對非同步程式設計的支援,下面就用.NET 1.0中提出的非同步程式設計模型(APM)來解決上面的問題,具體程式碼如下(註釋的部分通過獲得GUI執行緒的同步上文物件,然後同步呼叫同步上下文物件的post方法把要呼叫的方法交給GUI執行緒去處理,因為控制元件本來就是由GUI執行緒建立的,然後由它自己執行訪問控制元件的操作就不存在跨執行緒的問題了,程式中使用的是呼叫RichTextBox

控制元件的Invoke方式來非同步回撥訪問控制元件的方法,其實背後的原來和註釋部分是一樣的,呼叫RichTextBox控制元件的Invoke方法可以獲得建立RichTextBox控制元件的執行緒資訊(也就是前一種方式的同步上下文),然後讓Invoke回撥的方法在該執行緒上執行):

private void btnClick_Click(object sender, EventArgs e)
        {
            this.richTextBox1.Clear();
            btnClick.Enabled = false;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(GetResult, null);

            //// 捕捉呼叫執行緒的同步上下文派生物件
            //sc= SynchronizationContext.Current;
        }
   
        # region 使用APM實現非同步程式設計
        // 同步方法
        private string TestMethod()
        {       
            // 模擬做一些耗時的操作
            // 實際專案中可能是讀取一個大檔案或者從遠端伺服器中獲取資料等。
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(200);
            }

            return "點選我按鈕事件完成";
        }
       
        // 回撥方法
        private void GetResult(IAsyncResult result)
        {
            AsyncMethodCaller caller = (AsyncMethodCaller)((AsyncResult)result).AsyncDelegate;
            // 呼叫EndInvoke去等待非同步呼叫完成並且獲得返回值
            // 如果非同步呼叫尚未完成,則 EndInvoke 會一直阻止呼叫執行緒,直到非同步呼叫完成
            string resultvalue = caller.EndInvoke(result);
            //sc.Post(ShowState,resultvalue);
            richTextBox1.Invoke(showStateCallback, resultvalue);
        }

        // 顯示結果到richTextBox
        private void ShowState(object result)
        {
            richTextBox1.Text = result.ToString();
            btnClick.Enabled = true;
        }

        // 顯示結果到richTextBox
        //private void ShowState(string result)
        //{
        //    richTextBox1.Text = result;
        //    btnClick.Enabled = true;
        //}
        #endregion

執行的結果為:

四、C# 5.0 提供的async和await使非同步程式設計更簡單

 上面部分演示了使用傳統的非同步程式設計模型(APM)來解決同步程式碼所存在的問題,然而在.NET 2.0,.NET 4.0和.NET 4.5中,微軟都有推出新的方式來解決同步程式碼的問題,他們分別為基於事件的非同步模式,基於任務的非同步模式和提供async和await關鍵字來對非同步程式設計支援。關於前兩種非同步程式設計模式,在我前面的文章中都有介紹,大家可以檢視相關文章進行詳細地瞭解,本部分就C# 5.0中的async和await這兩個關鍵字如何實現非同步程式設計的問題來給大家介紹下。下面通過程式碼來了解下如何使用async和await關鍵字來實現非同步程式設計,並且大家也可以參看前面的部落格來對比理解使用async和await是非同步程式設計更簡單。

private async void btnClick_Click(object sender, EventArgs e)
        {
            long length = await AccessWebAsync();
           
            // 這裡可以做一些不依賴回覆的操作
            OtherWork();

            this.richTextBox1.Text += String.Format("\n 回覆的位元組長度為:  {0}.\r\n", length);
            txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString();
        }

        // 使用C# 5.0中提供的async 和await關鍵字來定義非同步方法
        // 從程式碼中可以看出C#5.0 中定義非同步方法就像定義同步方法一樣簡單。
        // 使用async 和await定義非同步方法不會建立新執行緒,
        // 它執行在現有執行緒上執行多個任務.
        // 此時不知道大家有沒有一個疑問的?在現有執行緒上(即UI執行緒上)執行一個耗時的操作時,
        // 為什麼不會堵塞UI執行緒的呢?
        // 這個問題的答案就是 當編譯器看到await關鍵字時,執行緒會
        private async Task<long> AccessWebAsync()
        {
            MemoryStream content = new MemoryStream();

            // 對MSDN發起一個Web請求
            HttpWebRequest webRequest = WebRequest.Create("http://msdn.microsoft.com/zh-cn/") as HttpWebRequest;
            if (webRequest != null)
            {
                // 返回回覆結果
                using (WebResponse response = await webRequest.GetResponseAsync())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        await responseStream.CopyToAsync(content);
                    }
                }
            }

            txbAsynMethodID.Text = Thread.CurrentThread.ManagedThreadId.ToString() ;
            return content.Length;
        }

        private void OtherWork()
        {
            this.richTextBox1.Text += "\r\n等待伺服器回覆中.................\n";
        }

執行結果如下:

五、async和await關鍵字剖析

我們對比下上面使用async和await關鍵字來實現非同步程式設計的程式碼和在第二部分的同步程式碼,有沒有發現使用async和await關鍵字的非同步實現和同步程式碼的實現很像,只是非同步實現中多了async和await關鍵字和呼叫的方法都多了async字尾而已。正是因為他們的實現很像,所以我在第四部分才命名為使用async和await使非同步程式設計更簡單,就像我們在寫同步程式碼一樣,並且程式碼的coding思路也是和同步程式碼一樣,這樣就避免考慮在APM中委託的回撥等複雜的問題,以及在EAP中考慮各種事件的定義。從程式碼部分我們可以看出async和await的使用確實很簡單,我們就如在寫同步程式碼一般,但是我很想知道編譯器到底給我們做了怎樣的處理的?並且從執行結果可以發現,執行非同步方法的執行緒和GUI執行緒的ID是一樣的,也就是說非同步方法的執行在GUI執行緒上,所以就不用像APM中那樣考慮跨執行緒訪問的問題了(因為通過委託的BeginInvoke方法來進行回撥方法時,回撥方法是線上程池執行緒上執行的)。下面就用反射工具看看編譯器把我們的原始碼編譯成什麼樣子的:

對於按鈕點選事件的程式碼來說,編譯器生成的背後程式碼卻是下面這樣的,完全和我們原始碼中的兩個樣:

// 編譯器為按鈕Click事件生成的程式碼
private void btnClick_Click(object sender, EventArgs e)
{
    <btnClick_Click>d__0 d__;
    d__.<>4__this = this;
    d__.sender = sender;
    d__.e = e;
    d__.<>t__builder = AsyncVoidMethodBuilder.Create();
    d__.<>1__state = -1;
    d__.<>t__builder.Start<<btnClick_Click>d__0>(ref d__);
}

看到上面的程式碼,作為程式設計師的我想說——編譯器你怎麼可以這樣呢?怎麼可以任意篡改我的程式碼呢?這樣不是侵犯我的版權了嗎?你要改最起碼應該告訴我一聲吧,如果我的原始碼看到它在編譯器中的實現是上面那樣的,我相信我的原始碼會說——難道我中了世間上最惡毒的面目全非腳嗎? 好吧,為了讓大家更好地理清編譯器背後到底做了什麼事情,下面就順著上面的程式碼摸瓜,我也來展示耍一套還我漂漂拳來幫助大家找到編譯器程式碼和原始碼的對應關係。我的分析思路為:

1、提出問題——我的click事件的原始碼到哪裡去了呢?

  從編譯器程式碼我們可以看到,前面的7句程式碼都是對某個類進行賦值的操作,最真正起作用的就是最後Start方法的呼叫。這裡又產生了幾個疑問——<btnClick_Click>d__0是什麼型別? 該型別中的<>t__builder欄位型別的Start方法到底是做什麼用的? 有了這兩個疑問,我們就點選<btnClick_Click>d__0(反射工具可以讓我們直接點選檢視)來看看它是什麼型別

// <btnClick_Click>d__0型別的定義,從下面程式碼可以看出它是一個結構體
// 該型別是編譯器生成的一個嵌入型別
// 看到該型別的實現有沒有讓你聯想到什麼?
private struct <btnClick_Click>d__0 : IAsyncStateMachine
{
    // Fields
    public int <>1__state;
    public Form1 <>4__this;
    public AsyncVoidMethodBuilder <>t__builder;
    private object <>t__stack;
    private TaskAwaiter<long> <>u__$awaiter2;
    public long <length>5__1;
    public EventArgs e;
    public object sender;

    // Methods
    private void MoveNext()
    {
        try
        {
            TaskAwaiter<long> CS$0$0001;
            bool <>t__doFinallyBodies = true;
            switch (this.<>1__state)
            {
                case -3:
                    goto Label_010E;

                case 0:
                    break;

                default:
            // 獲取用於等待Task(任務)的等待者。你要知道某個任務是否完成,我們就需要一個等待者物件對該任務進行一個監控,所以微軟就定義了一個等待者物件的
            // 從這裡可以看出,其實async和await關鍵字背後的實現原理是基於任務的非同步程式設計模式(TAP)
                    // 這裡程式碼是線上程池執行緒上執行的
                    CS$0$0001 = this.<>4__this.AccessWebAsync().GetAwaiter();
            // 如果任務完成就調轉到Label_007A部分的程式碼
                    if (CS$0$0001.IsCompleted)
                    {
                        goto Label_007A;
                    }
           
                    // 設定狀態為0為了退出回撥方法。
                    this.<>1__state = 0;
                    this.<>u__$awaiter2 = CS$0$0001;
            // 這個程式碼是做什麼用的呢?讓我們帶著問題看下面的分析
this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<long>, Form1.<btnClick_Click>d__0>(ref CS$0$0001, ref this);
                    <>t__doFinallyBodies = false;
            // 返回到呼叫執行緒,即GUI執行緒,這也是該方法不會堵塞GUI執行緒的原因,不管任務是否完成都返回到GUI執行緒
                    return;
            }
            // 當任務完成時,不會執行下面的程式碼,會直接執行Label_007A中程式碼
            CS$0$0001 = this.<>u__$awaiter2;
            this.<>u__$awaiter2 = new TaskAwaiter<long>();
            // 為了使再次回撥MoveNext程式碼
            this.<>1__state = -1;
        Label_007A:
            // 下面程式碼是在GUI執行緒上執行的
            CS$0$0001 = new TaskAwaiter<long>();
            long CS$0$0003 = CS$0$0001.GetResult();
            this.<length>5__1 = CS$0$0003;
        // 我們原始碼中的程式碼這裡的
            this.<>4__this.OtherWork();
            this.<>4__this.richTextBox1.Text = this.<>4__this.richTextBox1.Text + string.Format("\n 回覆的位元組長度為:  {0}.\r\n", this.<length>5__1);
            this.<>4__this.txbMainThreadID.Text = Thread.CurrentThread.ManagedThreadId.ToString();
        }
        catch (Exception <>t__ex)
        {
            this.<>1__state = -2;
            this.<>t__builder.SetException(<>t__ex);
            return;
        }
    Label_010E:
        this.<>1__state = -2;
        this.<>t__builder.SetResult();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine param0)
    {
        this.<>t__builder.SetStateMachine(param0);
    }
}

如果你看過我的迭代器專題的話,相信你肯定可以聯想到該結構體就是一個迭代器的一個實現,其主要方法就是MoveNext方法。從上面的程式碼的註釋應該可以幫助我們解決在第一步提到的第一個問題,即<btnClick_Click>d__0是什麼型別,下面就分析下第二個問題,從<btnClick_Click>d__0結構體的程式碼中可以發現<>t__builder的型別是AsyncVoidMethodBuilder型別,下面就看看它的Start方法的解釋——執行關聯狀態機的生成器,即呼叫該方法就可以開始執行狀態機,執行狀態機指的就是執行MoveNext方法(MoveNext方法中有我們原始碼中所有程式碼,這樣就把編譯器生成的Click方法與我們的原始碼關聯起來了)。從上面程式碼註釋中可以發現,當該MoveNext被呼叫時會立即還回到GUI執行緒中,同時也有這樣的疑問——剛開始呼叫MoveNext方法時,任務肯定是還沒有被完成的,但是我們輸出我們原始碼中的程式碼,必須等待任務完成(因為任務完成才能調轉到Label_007A中的程式碼),此時我們應該需要回調MoveNext方法來檢查任務是否完成,(就如迭代器中的,我們需要使用foreach語句一直呼叫MoveNext方法),然而我們在程式碼卻沒有找到回撥的任何程式碼啊? 對於這個疑問,回撥MoveNext方法肯定是存在的,只是首次看上面程式碼的朋友還沒有找到類似的語句而已,上面程式碼註釋中我提到了一個問題——這個程式碼是做什麼用的呢?讓我們帶著問題看下面的分析,其實註釋下面的程式碼就是起到回撥MoveNext方法的作用,AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted<TAwaiter, TStateMachine> 方法就是排程狀態機去執行MoveNext方法,從而也就解決了回撥MoveNext的疑問了。

相信大家從上面的解釋中可以找到原始碼與編譯器程式碼之間的對應關係了吧, 但是我在分析完上面的之後,又有一個疑問——當任務完成時,是如何退出MoveNext方法的呢?總不能讓其一直回撥吧,從上面的程式碼的註釋可以看出,當任務執行完成之後,會把<>1__state設定為0,當下次再回調MoveNext方法時就會直接退出方法,然而任務沒完成之前,同樣也會把<>1__state設定為0,但是Switch部分後面的程式碼又把<>1__state設定為-1,這樣就保證了在任務沒完成之前,MoveNext方法可以被重複回撥,當任務完成之後,<>1__state設定為-1的程式碼將不會執行,而是調轉到Label_007A部分。

經過上面的分析之後,相信大家也可以耍出一套還我漂漂拳去分析非同步方法AccessWebAsync(),其分析思路是和btnClick_Click的分析思路是一樣的.這裡就不重複囉嗦了。

分析完之後,下面再分享下幾個關於async和await常問的問題

問題一:是不是寫了async關鍵字的方法就代表該方法是非同步方法,不會堵塞執行緒呢?

  答: 不是的,對於只標識async關鍵字的(指在方法內沒有出現await關鍵字)的方法,呼叫執行緒會把該方法當成同步方法一樣執行,所以然而會堵塞GUI執行緒,只有當async和await關鍵字同時出現,該方法才被轉換為非同步方法處理。

問題二:“async”關鍵字會導致呼叫方法用執行緒池執行緒執行嗎?

  答: 不會,被async關鍵字標識的方法不會影響方法是同步還是非同步執行並完成,而是,它使方法可被分割成多個片段,其中一些片段可能非同步執行,這樣這個方法可能非同步完成。這些片段界限就出現在方法內部顯示使用”await”關鍵字的位置處。所以,如果在標記了”async”的方法中沒有顯示使用”await”,那麼該方法只有一個片段,並且將以同步方式執行並完成。在await關鍵字出現的前面部分程式碼和後面部分程式碼都是同步執行的(即在呼叫執行緒上執行的,也就是GUI執行緒,所以不存在跨執行緒訪問控制元件的問題),await關鍵處的程式碼片段是線上程池執行緒上執行。總結為——使用async和await關鍵字實現的非同步方法,此時的非同步方法被分成了多個程式碼片段去執行的,而不是像之前的非同步程式設計模型(APM)和EAP那樣,使用執行緒池執行緒去執行一整個方法

六、小結

  寫到這裡本專題的內容就介紹到這裡的,並且我也會把本專題的內容同步到之前的C#基礎知識系列文章索引,這樣我的C#特性系列也就完整了,並且該專題也是非同步程式設計的最後一篇專題,在後面的專題將為大家實現一個類似迅雷的多工多執行緒下載器,對於這個專題可能會用到並行程式設計的內容,所以接下面我為為大家分享下並行程式設計的內容。

原始碼下載地址:http://download.csdn.net/detail/lizhi3186575/6748137

相關推薦

[必須知道非同步程式設計]C# 5.0 特性——AsyncAwait使非同步程式設計簡單

本專題概要: 引言 同步程式碼存在的問題 傳統的非同步程式設計改善程式的響應 C# 5.0 提供的async和await使非同步程式設計更簡單  async和await關鍵字剖析 小結 一、引言  在之前的C#基礎知識系列文章中只介紹了從C#1.0到C#

C# 5.0 特性 asyncawait

1:  async和await 是 C# 5.0 的新特性 2:  需要.net Framework 4.5,或更高階版本才能使用   3: 需要VS2012,或更高階版本才能使用 4:它是用來配合 Task.Run(()=>{   這裡寫程式碼   });來使用

在WPF中使用C#6.0特性asyncawait

在C#6.0中 使用async與await 關鍵字很容易的實現非同步程式設計,而且程式碼可讀性比較高,很容易理解。這裡舉例的是從資料庫中讀取10w行資料。 下面看程式碼: xaml: <Window x:Class="WpfApplication1.MainWi

關於JAVA必須知道的那些事(三):繼承訪問修飾符

今天乘著還有一些時間,把上次拖欠的面向物件程式設計三大特性中遺留的繼承和多型給簡單說明一下。這一部分還是非常重要的,需要仔細思考。 繼承 繼承:它是一種類與類之間的關係,通過使用已存在的類作為基礎來建立新類。其中已存在的類稱為父類(或基類); 建立的新類稱為子類(或派生類)。簡單的就是子類繼

Asynchronous Programming with async and await (C#)用asyncawait實現非同步程式設計

You can avoid performance bottlenecks and enhance the overall responsiveness of your application by using asynchronous programming. Howe

C#7.0特性

reat href code 轉載 支持 als 有用 sharp object類 轉載自:http://www.cnblogs.com/GuZhenYin/p/6526041.html 微軟昨天發布了新的VS 2017 ..隨之而來的還有很多很多東西... .NET新版

C# 7.0 特性:本地方法

性能 erro 區別 visual html 修飾 之間 style ria C# 7.0:本地方法 VS 2017 的 C# 7.0 中引入了本地方法,本地方法是一種語法糖,允許我們在方法內定義本地方法。更加類似於函數式語言,但是,本質上還是基於面向對象實現的。 1.

詳解C#7.0特性

numeric base rdquo 字母 and throw cal odin png 1. out 變量(out variables) 以前我們使用out變量必須在使用前進行聲明,C# 7.0 給我們提供了一種更簡潔的語法 “使用時進行內聯聲明&r

C#4.0特性之協變與逆變實例分析

alt out thumb def 3.0 介紹 ted 路徑 運行 本文實例講述了C#4.0新特性的協變與逆變,有助於大家進一步掌握C#4.0程序設計。具體分析如下: 一、C#3.0以前的協變與逆變 如果你是第一次聽說這個兩個詞,別擔心,他們其實很常見。C#4.0中

轉載:C#7.0特性(VS2017可用)

AD mage 運行 str translate adc 微軟 returns false 前言 微軟昨天發布了新的VS 2017 ..隨之而來的還有很多很多東西... .NET新版本 ASP.NET新版本...等等..太多..實在沒消化.. 分享一下其實201

C#6.0C#7.0特性

  C#6.0,C#7.0新特性    C#6.0新特性 Auto-Property enhancements(自動屬性增強) Read-only auto-pr

[翻譯] C# 8.0 特性

原文: [翻譯] C# 8.0 新特性 原文: Building C# 8.0 [譯註:原文主標題如此,但內容大部分為新特性介紹,所以意譯標題為 "C# 8.0 新特性"] C# 的下一個主要版本是 8.0。我們已經為它工作了很長一段時間,即使我們構建併發布了次要版本 C# 7.1, 7.2 和 7.3,

async await非同步程式設計的學習

      async修改一個方法,表示其為非同步方法。而await表示等待一個非同步任務的執行。js方面,在es7中開始得以支援;而.net在c#5.0開始支援。本文章將分別簡單介紹他們在js和.net中的基本用法。 一、在js中的實現 js中

屬性賦初始值的四種寫法--C#6.0特性

1 介紹  在C# 6.0 語法出來前,屬性的賦初始值使用get;set;雖然已經相對於最原始的寫法簡潔了非常多,但是還不夠,還不夠,還不夠。。。今天剛好一直在想著前幾天朋友問我的有關屬性賦初始值的問題,便自己試了一下,感覺6.0提供了更好的寫法。 2 程式碼  (1)

藍芽|藍芽5.0特性

原文:http://www.wowotech.net/bluetooth/bluetooth_5_0_overview.html 快訊:藍芽5.0釋出(新特性速覽) 作者:wowo 釋出於:2016-12-8 11:05 分類:藍芽 1. 前言 20

C# 6.0 特性

Unity設定使用.net 4.6(Unity2018.2.2) c# 6.0是.net 4.6的一部分,unity預設使用的是.net 3.5,可以在Edit – Project Settings – Player中,將Scripting Runtime Version修改為Experimen

C# 7.0 特性(2): 本地方法

本文參考Roslyn專案中的Issue:#259. 簡而言之,【本地方法】就是在方法體內部定義一個方法。 其實咋眼一看,這個新特新並沒有什麼新意,因為目前大量C#的專案中,都可以使用delegate或基於delegate變形的各種方案(lambda, Fun,

C# 7.0 特性(1): 基於Tuple的“多”返回值方法

本文基於Roslyn專案中的Issue:#347 展開討論. 回顧 首先,提出一個問題,C#中,如何使一個方法可返回”多個”返回值? 我們先來回顧一下C#6.0 及更早版本的做法。 在C#中,通常我們有以下4種方式使一個方法返回多條資料。 使用 KeyVal

Spring學習總結(23)——Spring Framework 5.0 特性

Spring Framework 5.0 是自 2013年12月版本 4 釋出之後 Spring Framework 的第一個主發行版。Spring Framework 專案的領導人 Juergen

android 5.0特性

安卓5.0擁有非常多的其他新特性新功能,部分更新內容具體如下:   1、自適應各類尺寸螢幕,不止手機和平板,還包括汽車、手錶和電視機   2、各種觸控反饋效果更多   3、和Wear手錶裝置的更多互動   4、你的裝置你做主,可以控制提醒,可以忽略無