4、C# 5.0 新特性(.NET Framework 4.5 與 Visual Studio 2012 )

1、異步編程模型(APM)html

2、基於事件的異步編程模式(EAP) web

3、基於任務的異步模式(TAP),推薦使用 spring

4、C# 5.0 新特性——Async和Await使異步編程更簡單編程


1、C#新增的小功能

第一:綁定運算符:=

這個只是簡化了數據綁定,跟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)  

第三:case支持表達式:

這個是一個我很早就想若是能這樣就行了,沒想到在C#5.0裏就加入此功能,之前case裏只能寫一個具體的常量,而如今能夠加表達式了,靈活多了。異步

switch(myobj){
  case string.IsNullorEmpty(myotherobj):
 ..... 
  case myotherobj.Trim().Lower: 
 ....
}

第四:擴展屬性

咱們在C#3.0裏有擴展方法,那麼在C#5.0裏將會加入擴展屬性的感念,對照擴展方法,不難理解擴展屬性的概念了。如下爲擴展屬性的定義舉例:async

[Associate(string)]
public static int MyExtensionProperty { get;set;}

第五:支持null類型運算:

int x? = null;
int y? = x + 40;

Myobject obj = null;
Myotherobj obj2 = obj.MyProperty ??? new Myotherobj();

2、Asynchronous methods 異步方法

C# 5.0 提供的async和await使異步編程更簡單。異步編程

   .NET 4.5 的推出,對於C#又有了新特性的增長——就是C#5.0中async和await兩個關鍵字,這兩個關鍵字簡化了異步編程。函數

  1. 使用async修飾的方法被稱爲異步方法,這個方法調用時應該在前面加上await。
  2. 異步方法命名應該以Async結尾,這樣你們知道調用的時候使用await

async和await關鍵字只是編譯器的功能。編譯器最終會用Task類建立代碼。

一、建立任務

創建一個同步方法Greeting,該方法在等待一段時間後,返回一個字符串。

private string Greeting(int delay, string name)
 {
     System.Threading.Thread.Sleep(delay);     return string.Format("Hello, {0}.", name);
 }

定義一個方法GreetingAsync,可使方法異步化,其傳入的參數不作強制要求。基於任務的異步模式指定,並返回一個任務。注意,該方法返回的是Task<string>,定義了一個返回字符串的任務,與同步方法返回值一致。

private Task<string> GreetingAsync(string name, int delay = 3000)
{    return Task.Run<string>(() =>
    {        return Greeting(delay, name);
    });
}

二、調用異步方法

可使用await關鍵字調用返回任務的異步方法GreetingAsync。可是,使用await關鍵字的方法必需要用async關鍵字修飾符聲明。在GreetingAsync方法完成前,被async關鍵字修飾的方法內await關鍵字後面的代碼不會繼續執行。可是,啓動被async關鍵字修飾的方法的線程能夠被重用,而沒有被阻塞。

public async void CallerWithAsync()
{    string result = await GreetingAsync("Nigel", 2000);
    Console.WriteLine(result);
}

注意:async修飾符修飾只能用於返回Task或void的方法。不能做爲程序的入口點,即Main方法不能使用async修飾符。await修飾符只能用於返回Task的方法。

三、簡單實例

void Main()
{
    DisplayValue();
    System.Diagnostics.Debug.WriteLine("MyClass() End.");
}

public async void DisplayValue()
{
    double result = await GetValueAsync(1234.5, 1.01);//此處會開新線程處理GetValueAsync任務,而後方法立刻返回
                                                      //這以後的全部代碼都會被封裝成委託,在GetValueAsync任務完成時調用
    System.Diagnostics.Debug.WriteLine("Value is : " + result);
}

public Task<double> GetValueAsync(double num1, double num2)
{
    return Task.Run(() =>
    {
        for (int i = 0; i < 1000000; i++)
        {
            num1 = num1 / num2;
        }
        return num1;
    });
}

上面在MyClass的構造函數裏調用了async關鍵字標記的異步方法DisplayValue(),DisplayValue()方法裏執行了一個await關鍵字標記的異步任務GetValueAsync(),這個異步任務必須是以Task或者Task<TResult>做爲返回值的。

而咱們也看到,異步任務執行完成時實際返回的類型是void或者TResult,DisplayValue()方法裏await GetValueAsync()以後的全部代碼都會在異步任務完成時纔會執行。

DisplayValue()方法實際執行的代碼以下:

public void DisplayValue()
{
    System.Runtime.CompilerServices.TaskAwaiter<double> awaiter = GetValueAsync(1234.5, 1.01).GetAwaiter();
    awaiter.OnCompleted(() =>
        {
            double result = awaiter.GetResult();
            System.Diagnostics.Debug.WriteLine("Value is : " + result);
        });
} 

能夠看到,async和await關鍵字只是把上面的代碼變得更簡單易懂而已。

程序的輸出以下:

MyClass() End.

Value is : 2.47032822920623E-322

四、一個容易誤解的地方

考慮如下代碼:

async int M()
{
    return await F();
}


其中F()是一個異步方法,它返回的是Task<int>對象。

這段代碼事實上等價於:

async int M()
{
    int r = await F();
    return r;
}

注意和

async Task<int> M()
{
    return F();
}

區分。後面這段代碼是一個同步方法,它只會返回F()的真實返回值。

 

五、使用async 和await定義異步方法不會建立新線程, 它運行在現有線程上執行多個任務。

// 使用C# 5.0中提供的async 和await關鍵字來定義異步方法
// 從代碼中能夠看出C#5.0 中定義異步方法就像定義同步方法同樣簡單。
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";
}

運行結果以下:

13011212_81a426da5506437bb235516b077748bd[1]

3、async和await關鍵字剖析

咱們對比下上面使用async和await關鍵字來實現異步編程的代碼和在第二部分的同步代碼,有沒有發現使用async和await關鍵字的異步實現和同步代碼的實現很像,只是異步實現中多了async和await關鍵字和調用的方法都多了async後綴而已。正是由於他們的實現很像,因此我在第四部分才命名爲使用async和await使異步編程更簡單,就像咱們在寫同步代碼同樣,而且代碼的coding思路也是和同步代碼同樣,這樣就避免考慮在APM中委託的回調等複雜的問題,以及在EAP中考慮各類事件的定義。

下面再分享下幾個關於async和await常問的問題

問題一:是否是寫了async關鍵字的方法就表明該方法是異步方法,不會堵塞線程呢?

  答: 不是的,對於只標識async關鍵字的(指在方法內沒有出現await關鍵字)的方法,調用線程會把該方法當成同步方法同樣執行,因此然而會堵塞GUI線程,只有當async和await關鍵字同時出現,該方法才被轉換爲異步方法處理。

問題二:「async」關鍵字會致使調用方法用線程池線程運行嗎?

  答: 不會,被async關鍵字標識的方法不會影響方法是同步仍是異步運行並完成,而是,它使方法可被分割成多個片斷,其中一些片斷可能異步運行,這樣這個方法可能異步完成。這些片斷界限就出如今方法內部顯示使用」await」關鍵字的位置處。因此,若是在標記了」async」的方法中沒有顯示使用」await」,那麼該方法只有一個片斷,而且將以同步方式運行並完成。在await關鍵字出現的前面部分代碼和後面部分代碼都是同步執行的(即在調用線程上執行的,也就是GUI線程,因此不存在跨線程訪問控件的問題),await關鍵處的代碼片斷是在線程池線程上執行。總結爲——使用async和await關鍵字實現的異步方法,此時的異步方法被分紅了多個代碼片斷去執行的,而不是像以前的異步編程模型(APM)和EAP那樣,使用線程池線程去執行一整個方法。

4、Caller info attributes:調用時訪問調用者的信息

爲了便於調試,C#5.0提供了一種新特性:CallerInfoAttributes。它包括三個主要的類:

  1. [CallerMemberName] :返回調用函數的名稱。
  2. [CallerFilePath] :返回調用函數所在源文件全路徑信息 。
  3. [CallerLineNumber] :返回調用函數調用具體行號。

該功能主要是用於調試,示例代碼以下:

在本示例中,咱們添加了一個Log函數,以對程序進行日誌記錄,並在主函數中進行調用。

using System;
using System.Runtime.CompilerServices;

namespace TestPro
{
    class Program
    {
        public static void Main()
        {
            Log("Test.");
        } 

        // 對日誌消息進行記錄,同時全部內容均有默認值,若是獲取失敗,則使用默認值。
        public static void Log(string message,
            [CallerMemberName] string callerName = "unknown", 
            [CallerFilePath] string callerFilePath = "unknown", 
            [CallerLineNumber] int callerLineNumber = -1)
        {
            Console.WriteLine("Message: {0}", message);
            Console.WriteLine("Caller's Name: {0}", callerName);
            Console.WriteLine("Caller's FilePath: {0}", callerFilePath);
            Console.WriteLine("Caller's LineNumber: {0}", callerLineNumber);
        }
    }
}

程序執行之後會顯示如下內容。

Message: Test.
Caller Name: Main
Caller FilePath: C:\Users\Administrator\source\repos\TestPro\Program.cs
Caller Line number: 10
請按任意鍵繼續. . .
相關文章
相關標籤/搜索